本文是Qt中HTTP相关接口一个简单总结,主要是get/post请求以及表单提交等应用,HTTP相关知识自行百度。
目录
1.简介
2.认识QNetworkAccessManager
3.服务请求QNetworkRequest
4.服务响应QNetworkReply
5.表单数据QHttpMultiPart与QHttpPart
6.访问HTTPS
7.操作实例(代码链接)
8.参考
从Qt4.4开始,引入了QNetworkRequest、QNetworkReply 和 QNetworkAccessManager等类来进行HTTP、FTP的操作,替代之前的QFtp和QHttp。要使用这些类,先在pro文件中引入network模块:
QT += network
网络访问API围绕一个QNetworkAccessManager对象构建,该对象包含其发送的请求的通用配置和设置。它包含代理和缓存配置,以及与此类问题相关的信号,以及可用于监控网络操作进度的回复信号。由于QNetworkAccessManager基于QObject,因此只能从它所属的线程中使用它。
创建QNetworkAccessManager对象后,应用程序就可以使用它通过网络发送请求。 该类提供了一组标准函数,它们接收请求和可选数据,每个函数都返回一个QNetworkReply对象。返回的对象用于获取响应相应请求而返回的任何数据。
//构建一个manager对象
QNetworkAccessManager *manager = new QNetworkAccessManager(this);
//manager具有异步API,当http请求完成后,会通过finished信号进行通知
connect(manager,&QNetworkAccessManager::finished,this,&MyClass::replyFinished);
//发送异步get请求
manager->get(QNetworkRequest(QUrl("http://qt-project.org")));
//这里也可以用一个QEventLoop来等待请求完成,但是我更爱用槽函数
//QNetworkReply *reply=manager->get(request);
//QEventLoop eventLoop;
//connect(manager, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit);
//eventLoop.exec();
//QByteArray reply_data=reply->readAll();
QNetworkAccessManager将收到的请求排队。并行执行的请求数取决于协议。 目前,对于桌面平台上的HTTP协议,一个主机/端口组合并行执行6个请求。请求完成后,用户有责任在适当的时间删除QNetworkReply对象。不要在连接到finished()的插槽内直接删除它,可以使用deleteLater()函数。
通过Operation枚举可以看支持的请求方式:
可以看到,HTTP的常用请求方式都有对应的方法,并且提供了自定义的接口。
也可以通过supportedSchemes()方法查看支持的协议:
QNetworkAccessManager *manager=new QNetworkAccessManager(this);
qDebug()<supportedSchemes();
//这里可能是因为我没安装openssl,所以没有https
//("ftp", "file", "qrc", "http", "data")
要发起一个get/post请求,首先要构建一个QNetworkRequest对象作为参数,它包含一个URL和一些可用于修改请求的辅助信息。
//构建请求对象
QNetworkRequest request;
request.setUrl(QUrl("http://httpbin.org/get"));
request.setRawHeader("Content-Type","application/json");
//发送请求
//manager->get(request);
//manager->post(request, QByteArray());
//manager->put(request, QByteArray());
有时需要在url中携带参数,如果手动进行字符串拼接不是很方便。Qt5.0提供了 QUrlQuery类,可以很方便的拼接和解析url中的参数。
QUrl url("http://httpbin.org/get");
//拼接
QUrlQuery query;
query.addQueryItem("ie","utf-8");
url.setQuery(query);
qDebug()<
manager的finished信号发出后,就可以处理响应了。这里我使用的是连接manager的finished信号到槽函数的方式:
//connect(manager,&QNetworkAccessManager::finished,this,&MyClass::replyFinished);
//槽函数
void MyClass::replyFinished(QNetworkReply *reply)
{
if(reply->error()!=QNetworkReply::NoError){
//处理中的错误信息
qDebug()<<"reply error:"<errorString();
}else{
//请求方式
qDebug()<<"operation:"<operation();
//状态码
qDebug()<<"status code:"<attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
qDebug()<<"url:"<url();
//qDebug()<<"raw header:"<rawHeaderList();
//获取响应信息
const QByteArray reply_data=reply->readAll();
qDebug()<<"read all:"<deleteLater();
}
访问有些网页会返回301/302状态码,需要重新请求重定向的地址。
if (status_code == 301 || status_code == 302){
// Or the target URL if it was a redirect:
QVariant redirectionTargetUrl =reply->attribute(QNetworkRequest::RedirectionTargetAttribute);
//qDebug() << "redirection Url is " << redirectionTargetUrl.toString();
QUrl url(redirectionTargetUrl.toString());
manager->get(QNetworkRequest(url));
}
HTTP表单提交需要借助QHttpMultiPart与QHttpPart两个类,官方文档有个小示例(直接F1查看QHttpMultiPart),我这里稍加修改使之可以正常使用。
示例中图片的ContentDispositionHeader没有添加filename,导致测试时提交失败。示例中也没有设置Boundary分隔符,导致我提交到我们公司服务器时失败,加上就好了。这些应该都和服务器的设置有关。
void test(){
QNetworkAccessManager *manager=new QNetworkAccessManager(this);
//构建一个multiPart用于提交表单
//注意,multiPart请在请求完成后再删除
QHttpMultiPart *multiPart=new QHttpMultiPart(QHttpMultiPart::FormDataType);
//文本内容
QHttpPart namePart;
//Content-Type对照表详情百度http://tool.oschina.net/commons/
namePart.setHeader(QNetworkRequest::ContentTypeHeader,QVariant("text/plain"));
namePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"thename\";"));
namePart.setBody("gongjianbo");
QHttpPart agePart;
agePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"theage\";"));
agePart.setBody("27");
multiPart->append(namePart);
multiPart->append(agePart);
//文件内容
QHttpPart filePart;
filePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("text/plain")); //貌似我们公司我用application也行
//示例里没有filename,导致提交不成功
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"thefile\";filename=\"file.txt\";"));
QFile *textFile = new QFile("F:/Src/file.txt");
textFile->setParent(multiPart); //在删除reply时一并释放
if(textFile->open(QIODevice::ReadOnly)){
//要读取小块数据,请使用setBody(); 对于像图像这样的较大数据块,请使用setBodyDevice()。
filePart.setBodyDevice(textFile);
multiPart->append(filePart);
}
//图片内容
QHttpPart imagePart;
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("image/png"));
//示例里没有filename,导致提交不成功
imagePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant("form-data; name=\"theimage\";filename=\"image.png\";"));
QFile *imageFile = new QFile("F:/Src/image.png");
imageFile->setParent(multiPart); //在删除reply时一并释放
if(imageFile->open(QIODevice::ReadOnly)){
imagePart.setBodyDevice(imageFile);
multiPart->append(imagePart);
}
//在我们公司里使用的时候,没有Boundary也会导致提交不成功
multiPart->setBoundary("qtdata");
QNetworkRequest request(QUrl("http://httpbin.org/post"));
request.setRawHeader("Content-Type","multipart/form-data;boundary=qtdata");
//提交表单
QNetworkReply *reply=manager->post(request,multiPart);
multiPart->setParent(reply); //在删除reply时一并释放
QEventLoop eventLoop;
connect(manager, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
qDebug()<readAll();
reply->deleteLater();
}
支持HTTPS请求需要配置OpenSSL环境,Qt默认是不带SSl认证的,直接访问HTTPS会有错误信号。
//默认链接的ssl库版本
qDebug()<supportedSchemes();
//正常的reply接收
connect(manager,&QNetworkAccessManager::finished,
this,&MainWindow::slotFinish);
//ssl错误
connect(manager,&QNetworkAccessManager::sslErrors,
this,[=](QNetworkReply *reply,const QList &erros){
qDebug()<
(2019-8-28修改并完善https部分的内容)
以下是我测试的一些配置和测试(测试地址https://httpbin.org/post):
Qt5.9.8+MSVC2015-32bit+openssl1.0.x,将编译出来的libeay32.dll和ssleay32.dll放到exe同级目录能正常访问https(如果是在QtCreator中运行的话,放到安装环境的bin目录下也能使用)。
Qt5.9.8+MinGW-32bit+openssl1.0.x,复制Qt Tools目录下的libeay32.dll和ssleay32.dll放到exe同级目录能正常访问https(我的在D:\Qt\Qt5.9.8\Tools\QtCreator\bin目录)。
Qt5.12.4+MSVC2015-64bit,将编译出来的libcrypto-1_1-x64.dll和libssl-1_1-x64.dll放到exe同级目录就能正常访问https了(我没放openssl的库貌似也能访问,我在家又测试了一遍,发现也是msvc 64bit的不用dll也能访问https,但是32bit的就不行,不知道是系统里有这个库还是啥原因)。
Qt5.13.0+MSVC2019-32bit,将编译出来的libcrypto-1_1.dll和libssl-1_1.dll放到exe同级目录就能正常访问https了。
我的openssl编译记录:https://blog.csdn.net/gongjianbo1992/article/details/100115710
我生成的dll百度云连接:https://pan.baidu.com/s/1ALvI7FStr9YIx942QQTv1Q
提取码:0q96
这里借助httpbin.org网站写一个简单的例子。httpbin.org是用 Python + Flask编写的一个开源项目,这个网站能测试 HTTP 请求和响应的各种信息,比如 cookie、ip、headers 和登录验证等,且支持 GET、POST 等多种方法,对开发和测试很有帮助。
我的示例链接:https://github.com/gongjianbo/MyTestCode/tree/master/Qt/Qt5HttpDemo
参考文档:https://doc.qt.io/qt-5/qnetworkaccessmanager.html
参考博客(HTTP):https://blog.csdn.net/liang19890820/article/details/52535755
参考博客(表单):https://blog.csdn.net/liang19890820/article/details/52548717
参考博客(表单):https://blog.csdn.net/u011728480/article/details/76851172?locationNum=1&fps=1
参考博客(HTTPS):https://www.cnblogs.com/jk-Huan/p/8953541.html
OpenSSL编译:https://blog.csdn.net/gongjianbo1992/article/details/100115710