需求
Qt 代码中使用QNetworkRequest、QNetworkReply进行http请求,能不能整理成一个通用的方法或者类。
实现
HttpClient类
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QUrlQuery>
#include <QByteArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QDebug>
#include <QEventLoop>
class HttpClient : public QObject
{
Q_OBJECT
public:
explicit HttpClient(QObject *parent = nullptr)
: QObject(parent), networkManager(new QNetworkAccessManager(this))
{
connect(networkManager, &QNetworkAccessManager::finished, this, &HttpClient::onReplyFinished);
}
void get(const QUrl &url, const QUrlQuery &query = QUrlQuery())
{
QUrl fullUrl = url;
fullUrl.setQuery(query);
QNetworkRequest request(fullUrl);
networkManager->get(request);
eventLoot->exec();
}
void post(const QUrl &url, const QByteArray &data)
{
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
networkManager->post(request, data);
eventLoot->exec();
}
signals:
void requestFinished(const QByteArray &responseData, QNetworkReply::NetworkError error);
private slots:
void onReplyFinished(QNetworkReply *reply)
{
if (reply->error() == QNetworkReply::NoError) {
QByteArray responseData = reply->readAll();
emit requestFinished(responseData, QNetworkReply::NoError);
} else {
emit requestFinished(QByteArray(), reply->error());
}
reply->deleteLater();
}
private:
QNetworkAccessManager *networkManager;
QEventLoop eventLoop;
};
HttpClient中使用了一个QEventLoop,为了使发送的事件得到及时的处理,不加这个的话,测试发现半天都不能调到onReplyFinished槽函数中。
使用
#include <QCoreApplication>
#include <QUrl>
#include <QUrlQuery>
#include <QJsonDocument>
#include <QJsonObject>
#include <QByteArray>
#include "HttpClient.h"
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
HttpClient client;
// 发送GET请求
QUrl getUrl("https://jsonplaceholder.typicode.com/posts/1");
QObject::connect(&client, &HttpClient::requestFinished, [](const QByteArray &responseData, QNetworkReply::NetworkError error) {
if (error == QNetworkReply::NoError) {
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
QJsonObject jsonObj = jsonDoc.object();
qDebug() << "GET Response:" << jsonObj;
} else {
qDebug() << "GET Error:" << error;
}
});
client.get(getUrl);
// 发送POST请求
QUrl postUrl("https://jsonplaceholder.typicode.com/posts");
QJsonObject postData;
postData["title"] = "foo";
postData["body"] = "bar";
postData["userId"] = 1;
QByteArray postDataBytes = QJsonDocument(postData).toJson();
QObject::connect(&client, &HttpClient::requestFinished, [](const QByteArray &responseData, QNetworkReply::NetworkError error) {
if (error == QNetworkReply::NoError) {
QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
QJsonObject jsonObj = jsonDoc.object();
qDebug() << "POST Response:" << jsonObj;
} else {
qDebug() << "POST Error:" << error;
}
});
client.post(postUrl, postDataBytes);
return a.exec();
}
优化
上述代码可能存在的问题,如果有嵌套http请求,可能会造成请求递归调用,最终导致栈溢出。
修改点在于,连接信号槽的时候,使用发送请求时返回的QNetworkReply去连接对应处理的槽函数。
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QUrl>
#include <QUrlQuery>
#include <QByteArray>
#include <QJsonObject>
#include <QJsonDocument>
#include <QDebug>
#include <QEventLoop>
class HttpClient : public QObject
{
Q_OBJECT
public:
explicit HttpClient(QObject *parent = nullptr)
: QObject(parent), networkManager(new QNetworkAccessManager(this))
{
//connect(networkManager, &QNetworkAccessManager::finished, this, &HttpClient::onReplyFinished);
}
void get(const QUrl &url, const QUrlQuery &query = QUrlQuery())
{
QUrl fullUrl = url;
fullUrl.setQuery(query);
QNetworkRequest request(fullUrl);
QNetworkReply *reply = networkManager->get(request);
//这里进行连接
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
connect(reply, &QNetworkReply::finished, this, [this, reply]()
{
onReplyFinished(reply);
});
eventLoot->exec();
}
void post(const QUrl &url, const QByteArray &data)
{
QNetworkRequest request(url);
request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
QNetworkReply *reply = networkManager->post(request, data);
//这里进行连接
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
connect(reply, &QNetworkReply::finished, this, [this, reply]()
{
onReplyFinished(reply);
});
eventLoot->exec();
}
signals:
void requestFinished(const QByteArray &responseData, QNetworkReply::NetworkError error);
private slots:
void onReplyFinished(QNetworkReply *reply)
{
if (reply->error() == QNetworkReply::NoError) {
QByteArray responseData = reply->readAll();
emit requestFinished(responseData, QNetworkReply::NoError);
} else {
emit requestFinished(QByteArray(), reply->error());
}
reply->deleteLater();
}
private:
QNetworkAccessManager *networkManager;
QEventLoop eventLoop;
};