最近接触的QT项目与平台对接时需要用到WebService。博主从毕业起大部分工作都是在做桌面开发,所以对WebService只能说是一知半解。唯一的接触就是在.NET平台下编写,发布和调用WebService。这其中大部分工作微软都帮我们做好了,我们只需添加web引用,就像引用本地的库一样。不得不说VS确实强大且易用性好。但是很多事情都是两面性的。就像现在习惯了vs的方便,当面临linux C++的WebService调用时,博主直接就懵逼了。闲话少说,进入正题。

最初的尝试 qtsoap

由于是QT项目,一向懒惰的博主首先想到的是去找QT下的开源方案。几番查找,找到了这个4、5年前的开源项目qtsoap

通过官方示例和各种查阅文档。最终博主终于完成一个调用类,结构如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35

#ifndef WEBSERVICE_H
#define WEBSERVICE_H
#include <qtsoap.h>
class WebService:public QObject
{
Q_OBJECT
public:
/**********************************************
* description: Constructor
* parameter: serviceUrl webservice url
* opration request method
* ********************************************/
WebService(QString serviceUrl,QString operation);
~WebService();
/*************************************
* description: Submit Request
* parameter: argument for operation
* ***********************************/
void SubmitRequest(QString argument);

private slots:
/***********************************************
* description: Response after operation
* parameter: responseMessage
* *********************************************/
void getResponse(const QtSoapMessage &response);

private:
//服务地址
QString serviceUrl;
//执行的操作名称
QString operation;
QtSoapHttpTransport http;
};

只有两个函数:提交请求和获得响应,博主高高兴兴的编译 运行,

又是一脸懵,无论怎么检查,响应永远是Network transport error。没办法开始翻源码。当我看到要翻3000+行C++代码时,我的内心是拒绝的。

无奈之下改翻头文件,XMLschema和namespace深深的吸引了我

这地址1998和1999,主观上以我对自己的了解,我是不会再改这货了。最后还是去QT开源社区的群咨询了一下下。许久只得到了以下回复其余都是乱扯:

1
qtsoap,我已经放弃了。

最后还是默默的关掉了项目,寻求其他方案。

进一步了解WebService和QNetworkAccessManager

无奈之下只能进一步开始了解WebService。什么是WebService呢?简单的说就是一种可以接受和相应网络上其他系统传递的请求,轻量的通讯技术,通过soap协议在web上提供服务。这里就不做详细解释了,想了解的同学可以自行搜索或参阅文末链接。基于对WebService的进一步了解,也就是说我们只需要通过网络向WebService发送soap消息请求就可以了。于是下一步就是解决QT下的网络通信了。于是主角QNetworkAccessManager登场。在了解了如何使用QNetworkAccessManager发送post和get请求后懒癌晚期的还是选择了寻找现成的方法。最后找到了这个:QNetworkAccessManager调用WebService
因为目标WebService需要客户端传递xml格式的字符串,博主在以上方案上修改了部分代码又加了两个函数。源码奉上:

头文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94

/** *****************************************************
* QWebService
*
* 通过http post或get请求调用WebService
*
* ******************************************************/
#ifndef QWEBSERVICE_H
#define QWEBSERVICE_H

#include <QTimer>
#include <QThread>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
/**
* xml字符串,转义符替换。
**/
void Xml2Esc(QString &xmlString);
void Esc2Xml(QString &escString);
class NetDataListenerThread:public QThread
{
Q_OBJECT
public:
NetDataListenerThread(QNetworkAccessManager *manager,QNetworkReply * reply,int timeoutms,QObject *parent = 0);
~NetDataListenerThread();
QByteArray getBa() const;
bool getIsWaitTimeOut() const;
protected:
virtual void run();
private:
bool waitForConnect();
protected slots:
void slot_waitTimeout();
signals:
void sig_waitTimeout();
private:
QNetworkAccessManager *mManager;
int m_TimeOutms;
bool m_waitTimeOut;
bool m_isWaitTimeOut;
QNetworkReply * m_reply;
};
/**
* @brief The WebServiceHelp class webservice调用帮助类
* @author zp
*/
class WebServiceHelp:public QObject
{
Q_OBJECT
private:
QString postXml = "<?xml version=\"1.0\" encoding=\"utf-8\"?><soap:Envelope xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:soap=\"http://schemas.xmlsoap.org/soap/envelope/\"><soap:Body><toTraditionalChinese xmlns=\"http://webxml.com.cn/\"><sText>ARGUMENT</sText></toTraditionalChinese></soap:Body></soap:Envelope>";
class Dispose
{
public:
~Dispose();
};
public:
static WebServiceHelp * getInstance();
public:
/**
* @brief sendGetRequest 发送Get请求
* @param urlStr url
* @param ba 返回数据
* @param timeOutms 超时时间
* @return 返回错误代码
*/
QNetworkReply::NetworkError sendGetRequest(QString urlStr, QByteArray & ba, int timeOutms=3000);
/**
* @brief sendPostRequest 发送post请求
* @param website 网址
* @param methodName 方法名称
* @param param 参数
* @param postBa post数据
* @param code 返回代码
* @param message 返回信息
* @param data 返回数据
* @param timeOutms 超时时间
* @return 返回错误代码
*/
QNetworkReply::NetworkError sendPostRequest(QString website,const QString argument, QByteArray& retBa, int timeOutms=3000);
protected slots:
void slot_error(QNetworkReply::NetworkError code);
private:
static WebServiceHelp * serverHelp;
static WebServiceHelp::Dispose disp;
QNetworkAccessManager *manager;
QNetworkReply::NetworkError m_errCode;
private:
WebServiceHelp(QObject *parent=0);
~WebServiceHelp();
};

#endif // QWEBSERVICE_H

类文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
   #include "qwebservice.h"
#include <QEventLoop>

NetDataListenerThread::NetDataListenerThread(QNetworkAccessManager *manager, QNetworkReply *reply, int timeoutms, QObject *parent)
:QThread(parent),mManager(manager),m_TimeOutms(timeoutms),m_reply(reply)
{
m_waitTimeOut = false;
m_isWaitTimeOut = false;
}
NetDataListenerThread::~NetDataListenerThread()
{
}
void NetDataListenerThread::run()
{
m_isWaitTimeOut = waitForConnect();
}
bool NetDataListenerThread::waitForConnect()
{
QEventLoop eventLoop;
QTimer * timer = NULL;
m_waitTimeOut = false;
bool bWaitTimeOut = false;
if (m_TimeOutms > 0)
{
timer = new QTimer;
connect(timer, SIGNAL(timeout()), this, SLOT(slot_waitTimeout()));
timer->setSingleShot(true);
timer->start(m_TimeOutms);
connect(this, SIGNAL(sig_waitTimeout()), &eventLoop, SLOT(quit()));
}
connect(mManager, SIGNAL(finished(QNetworkReply *)), &eventLoop, SLOT(quit()));
if (m_reply != NULL)
{
connect(m_reply, SIGNAL(readyRead()), &eventLoop, SLOT(quit()));
}
eventLoop.exec();
if (timer != NULL)
{
timer->stop();
delete timer;
timer = NULL;
}
bWaitTimeOut = m_waitTimeOut;
m_waitTimeOut = false;
return !bWaitTimeOut;
}
void NetDataListenerThread::slot_waitTimeout()
{
m_waitTimeOut = true;
emit sig_waitTimeout();
}
bool NetDataListenerThread::getIsWaitTimeOut() const
{
return m_isWaitTimeOut;
}
WebServiceHelp * WebServiceHelp::serverHelp = NULL;
WebServiceHelp::Dispose WebServiceHelp::disp;
WebServiceHelp::WebServiceHelp(QObject *parent):QObject(parent)
{
manager = new QNetworkAccessManager();
m_errCode= QNetworkReply::NoError;
}
WebServiceHelp::~WebServiceHelp()
{
manager->deleteLater();
}
WebServiceHelp *WebServiceHelp::getInstance()
{
if(serverHelp==NULL)
{
serverHelp = new WebServiceHelp();
}
return serverHelp;
}
QNetworkReply::NetworkError WebServiceHelp::sendGetRequest(QString urlStr, QByteArray & ba,int timeOutms)
{
QNetworkReply::NetworkError retError = QNetworkReply::NoError;
m_errCode= QNetworkReply::NoError;
QNetworkRequest request;
QUrl url(urlStr);
request.setUrl(url);
QNetworkReply *reply = manager->get(request);
connect(reply,static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),this,&WebServiceHelp::slot_error);
QEventLoop eventLoop;
NetDataListenerThread * thread = new NetDataListenerThread(manager,reply,timeOutms);
connect(thread, &NetDataListenerThread::finished,&eventLoop,&QEventLoop::quit);
thread->start();
eventLoop.exec();
if(thread->getIsWaitTimeOut())
{
ba = reply->readAll();
}
else
{
m_errCode=QNetworkReply::TimeoutError;
}
thread->deleteLater();
delete reply;
delete thread;
thread = NULL;
retError = m_errCode;
m_errCode= QNetworkReply::NoError;
return retError;
}
QNetworkReply::NetworkError WebServiceHelp:: sendPostRequest(QString website, const QString argument, QByteArray &retBa, int timeOutms)
{
QString urlStr =website;
QByteArray postBa=postXml.replace("ARGUMENT",argument).toUtf8();
qDebug()<<postBa;
QNetworkReply::NetworkError retError = QNetworkReply::NoError;
m_errCode= QNetworkReply::NoError;
QNetworkRequest request;
QSslConfiguration config;
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1SslV3);
request.setSslConfiguration(config);
QUrl url(urlStr);
request.setUrl(url);
request.setHeader(QNetworkRequest::ContentTypeHeader,"text/xml; charset=utf-8");
request.setHeader(QNetworkRequest::ContentLengthHeader,postBa.length());
QNetworkReply *reply = manager->post(request,postBa);
connect(reply,static_cast<void (QNetworkReply::*)(QNetworkReply::NetworkError)>(&QNetworkReply::error),this,&WebServiceHelp::slot_error);
QEventLoop eventLoop;
NetDataListenerThread * thread = new NetDataListenerThread(manager,reply,timeOutms);
connect(thread, &NetDataListenerThread::finished,&eventLoop,&QEventLoop::quit);
thread->start();
eventLoop.exec();
if(thread->getIsWaitTimeOut())
{
retBa = reply->readAll();
}
else
{
m_errCode=QNetworkReply::TimeoutError;
}
thread->deleteLater();
delete reply;
delete thread;
thread = NULL;
retError = m_errCode;
m_errCode= QNetworkReply::NoError;
return retError;
}
void WebServiceHelp::slot_error(QNetworkReply::NetworkError code)
{
m_errCode = code;
}
WebServiceHelp::Dispose::~Dispose()
{
if(WebServiceHelp::serverHelp!=NULL)
{
delete WebServiceHelp::serverHelp;
WebServiceHelp::serverHelp=NULL;
}
}

void Xml2Esc(QString &xmlString)
{
xmlString.replace("<","&lt;");
xmlString.replace(">","&gt;");
xmlString.replace("&","&amp;");
xmlString.replace("'","&apos;");
xmlString.replace("\"","&quot;");
}
void Esc2Xml(QString &escString)
{
escString.replace("&lt;","<");
escString.replace("&gt;",">");
escString.replace("&amp;","&");
escString.replace("&apos;","'");
escString.replace("&quot;","\"");
}

这样我们只需在项目加入以上代码,直接调用函数发送请求就ok了

1
QNetworkReply::NetworkError ret=WebServiceHelp::getInstance()->sendPostRequest(url,argement,retba);

最终的选择 gsoap

虽然博主已经通过发送post请求实现了WebService的调用,但开会时平台方还是建议用gsoap实现WebService服务或调用WebService。最终博主还是默默的将gsoap下载下来。默默的查了查C++的调用方法。然后默默的将项目替换成使用gsoap调用WebService。
关于gsoap的C++使用方法博主会有单独的文章介绍。今天就先写到这里,出差在宾馆写这些已经很佩服我自己了。继续加油吧。。。

附链

最后更新: 2018年03月25日 15:16

原始链接: http://rickeryan.tk/2017/06/21/cpp-call-webservice/

× 打赏作者~
打赏二维码