写在前面:我发现我好像中毒了,做什么东西一个想的都是能不能用web去实现。之前组态王课程设计的时候,玩了一个服务器发布组态王工程;管理信息系统大作业设计的时候,写了一个webGIS,而这次,我接到的任务是写一个小摄像头的上位机,于是又想到了能不能实现在线更新等与网络交互相关的功能…本文内容部分转自https://blog.csdn.net/liang19890820/article/details/52535755
构造请求
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
// URL QString baseUrl = "http://www.csdn.net/"; // 构造请求 QNetworkRequest request; request.setUrl(QUrl(baseUrl)); QNetworkAccessManager *manager = new QNetworkAccessManager(this); // 发送请求 QNetworkReply *pReplay = manager->get(request); // 开启一个局部的事件循环,等待响应结束,退出 QEventLoop eventLoop; QObject::connect(manager, &QNetworkAccessManager::finished, &eventLoop, &QEventLoop::quit); eventLoop.exec(); // 获取响应信息 QByteArray bytes = pReplay->readAll(); qDebug() << bytes; |
因为请求的过程是异步的,所以此时使用 QEventLoop 开启一个局部的事件循环,等待响应结束,事件循环退出。
注意:开启事件循环的同时,程序界面将不会响应用户操作(界面被阻塞)。
简便的 API 意味着所有 HTTP 请求类型都是显而易见的。那么其他 HTTP 请求类型:POST,PUT 又是如何的呢?都是一样的简单:
1 2 |
QNetworkReply *pPostReplay = manager->post(request, QByteArray()); QNetworkReply *pPutReplay = manager->put(request, QByteArray()); |
在这里,我果断踩了一个坑
获取请求的时候,需要放在一个事件循环中
即需要上面程序中的局部事件循环,否则无法读取(5555….因为一开始不懂这个,导致浪费了4个小时查错)
下面是stackoverflow上一个精彩的回答
You don’t have an event loop in your thread’s
run()
method, so there’s no chance of any networking code working.You should put all of your code into a
QObject
, and move it to a generic thread. The default implementation ofQThread::run()
spins an event loop.
传递 URL 参数
1 2 3 4 5 6 7 8 9 10 |
// URL QString baseUrl = "http://www.zhihu.com/search"; QUrl url(baseUrl); // key-value 对 QUrlQuery query; query.addQueryItem("type", "content"); query.addQueryItem("q", "Qt"); url.setQuery(query); |
最终的请求地址为
“http://www.zhihu.com/search?type=content&q=Qt”
定制请求头
如果你想为请求添加 HTTP 头部,只要简单地调用 setHeader() 就可以了。
例如,发送的请求时,使用的 User-Agent 是 Mozilla/5.0
, 为了方便以后追踪版本信息,可以将软件的版本信息写入到 User-Agent 中。
1 2 |
QNetworkRequest request; request.setHeader(QNetworkRequest::UserAgentHeader, "my-app/0.0.1"); |
响应状态码
1 2 3 4 |
QVariant variant = pReplay->attribute(QNetworkRequest::HttpStatusCodeAttribute); if (variant.isValid()) qDebug() << variant.toInt(); // 200 |
响应头
1 2 3 4 |
QVariant variant = pReplay->header(QNetworkRequest::ContentTypeHeader); if (variant.isValid()) qDebug() << variant.toString(); // "text/html; charset=utf-8" |
响应内容
要获取响应的内容,可以调用 readAll(),由于上述的 POST 请求返回的数据为 Json 格式,将响应结果先转化为 Json,然后再对其解析:
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 |
void replyFinished(QNetworkReply *reply) { // 获取响应信息 QByteArray bytes = reply->readAll(); QJsonParseError jsonError; QJsonDocument doucment = QJsonDocument::fromJson(bytes, &jsonError); if (jsonError.error != QJsonParseError::NoError) { qDebug() << QStringLiteral("解析Json失败"); return; } // 解析Json if (doucment.isObject()) { QJsonObject obj = doucment.object(); QJsonValue value; if (obj.contains("data")) { value = obj.take("data"); if (value.isString()) { QString data = value.toString(); qDebug() << data; } } } } |
响应的内容可以是 HTML 页面,也可以是字符串、Json、XML等。最上面所发送的 GET 请求 获取的就是 CSDN 的首页 HTML。
错误
如果请求的处理过程中遇到错误(如:DNS 查询失败、拒绝连接等)时,则会产生一个 QNetworkReply::NetworkError。
发送请求后,由于主机名无效,必然会发生错误,根据 error() 来判断:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 |
QNetworkReply::NetworkError error = pReplay->error(); switch (error) { case QNetworkReply::ConnectionRefusedError: qDebug() << QStringLiteral("远程服务器拒绝连接"); break; case QNetworkReply::HostNotFoundError: qDebug() << QStringLiteral("远程主机名未找到(无效主机名)"); break; case QNetworkReply::TooManyRedirectsError: qDebug() << QStringLiteral("请求超过了设定的最大重定向次数"); break; deafult: qDebug() << QStringLiteral("未知错误"); } // "远程主机名未找到(无效主机名)" |