最近在做基于Https协议的项目编程,以下实现一个基于QT的Https的客户端
本QT4.7.0+VS2008+Win10.
主要使用了以下三个类:
http访问的类:QNetworkAccessManager
http请求的类:QNetworkRequest
http回复的类:QNetworkReply
使用上面三个类可以实现http客户端所有功能,简单地调用如下:
reply = manager->get(request);
reply = manager->post(request, data);
QNetworkAccessManager的调用方法,根据不同的类型可以调用不同的方法:
QNetworkReply * deleteResource ( const QNetworkRequest & request )
QNetworkReply * get ( const QNetworkRequest & request )
QNetworkReply * head ( const QNetworkRequest & request )
QNetworkReply * post ( const QNetworkRequest & request, QIODevice * data )
QNetworkReply * post ( const QNetworkRequest & request, const QByteArray & data )
QNetworkReply * put ( const QNetworkRequest & request, QIODevice * data )
QNetworkReply * put ( const QNetworkRequest & request, const QByteArray & data )
QNetworkRequest request;
QUrlQuery urlQuery;
QString urlString;
QUrl url;
/* url */
urlString.append("https://httpbin.org/");
url.setUrl(urlString);
/* query */
urlQuery.addQueryItem("id", "123456");
urlQuery.addQueryItem("time", "2019-10-01 10:30:00");
url.setQuery(urlQuery);
//由于QT4.7.0中不支持QUrlQuery类,所以这里可采用手动拼接方式
if (!url.contains("?", Qt::CaseInsensitive))
{
url.append("?");
}
else
{
url.append("&");
}
url.append(Key + "=" + Value);
//拼接最后如下
https://www.example.com?key1=value1&key2=value2
//也可以使用QUrl的addQueryItem函数
//QT4的写法:
QUrl url("http://www.example.com");
url.addQueryItem("key1", "value1");
url.addQueryItem("key2", "value2");
//QT5的写法
QUrl url("http://www.example.com");
QUrlQuery urlQuery(url);
urlQuery.addQueryItem("key1", "value1");
urlQuery.addQueryItem("key2", "value2");
url.setQuery(urlQuery);
//QT4和QT5的混合写法:
QUrl url("http://www.example.com");
#if (QT_VERSION <= QT_VERSION_CHECK(5,0,0)) // Qt4
url.addQueryItem("key1", "value1");
url.addQueryItem("key2", "value2");
#else
QUrlQuery urlQuery(url);
urlQuery.addQueryItem("key1", "value1");
urlQuery.addQueryItem("key2", "value2");
url.setQuery(urlQuery);
#endif
request.setUrl(url);
追加或修改头Head数据
void QNetworkRequest::setRawHeader ( const QByteArray & headerName, const QByteArray & headerValue )
QNetworkRequest request;
request.setRawHeader("User-Agent", "QHttpsC 1.0");
request.setRawHeader("Authorization", "Basic WHtpsWeeSFW4=");
request.setRawHeader("Last-Modified", "Sun, 06 Nov 2019 08:49:37 GMT");
//QNetworkReply * post ( const QNetworkRequest & request, const QByteArray & data )
reply = manager->post(request, data);//data数据可能是Json格式串
/*200 服务器成功返回网页
404 请求的网页不存在
503 服务不可用*/
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
int retCode = statusCode.toInt();
//QList QNetworkReply::rawHeaderList () const //取到所有的头的键列表
QList list = reply->rawHeaderList();
//QByteArray QNetworkReply::rawHeader ( const QByteArray & headerName ) const //通过键名获取对应的值
for (int i = 0; i < list.size(); i++)
{
qDebug() << "header: " << list.at(i).data() << " " << reply->rawHeader(list.at(i));
}
//QByteArray QIODevice::readAll ()
QByteArray resp = reply->readAll();
支持HTTPS请求需要配置OpenSSL环境,Qt默认是不带SSl认证的,直接访问HTTPS会有错误信号。 环境配置:
需要对应的库文件 libeay32.dll和ssleay32.dll。需要下载libeay32.dll 和 ssleay32.dll两个库文件,将这两个库文件拷贝到程序生成目录下(即生成exe的同级目录)或者拷贝到QtNetwork模块的库文件目录中。可以在本地电脑搜索这两个库文件,直接拷贝就行。
并且在发送请求前设置一下:
QSslConfiguration conf = request.sslConfiguration();
conf.setPeerVerifyMode(QSslSocket::VerifyNone);
conf.setProtocol(QSsl::AnyProtocol);
request.setSslConfiguration(conf);
测试服务器:https://httpbin.org/
http请求的认证信息通过增加认证头来实现。用户名和密码常用的加密方式有basic和digest加密
Basic加密头格式:
Authorization: Basic base64(username:password)
Digest加密头格式:
Authorization: Digest MD5(username:realm:password)
这里设计了一个信号槽,如果访问需要认证,将自动调用需要校验的数据,用户名和密码;这里是在发送数据前,需要将认证信息传进去。
/*void QNetworkAccessManager::authenticationRequired ( QNetworkReply * reply, QAuthenticator * authenticator ) [signal]*/
manager = new QNetworkAccessManager(this);
connect(manager,SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),this, SLOT(authRequiredReply(QNetworkReply*,QAuthenticator*)));
//实现的槽函数如下
void TestHttpClient::authRequiredReply(QNetworkReply *reply, QAuthenticator *auth)
{
QString username = ui->username->text();
QString password = ui->password->text();
if (username.isEmpty() || password.isEmpty()) {
return ;
}
auth->setUser(username);
auth->setPassword(password);
}