本篇项目主要是通过QT的网络API接口实现一个http客户端的工具qhttpc。
qhttpc工具,支持http协议,实现了http的数据请求和回复数据解析。暂不支持https,可自行配置。
工具的源码链接: github/qhttpc。
在.pro文件中增加网络组件支持
QT += network
http访问的类:QNetworkAccessManager
http请求的类:QNetworkRequest
http回复的类:QNetworkReply
使用上述三个类即可实现所有的http客户端的功能,用法可以简单概括为:
reply = manager->get(request);
reply = manager->post(request, data);
(1)选择请求method
使用QNetwofkAccessManager
类发起请求,最终调用方法根据请求的method选择,有以下几种:
QNetworkReply *head(const QNetworkRequest &request);
QNetworkReply *get(const QNetworkRequest &request);
QNetworkReply *post(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *post(const QNetworkRequest &request, const QByteArray &data);
QNetworkReply *post(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *put(const QNetworkRequest &request, QIODevice *data);
QNetworkReply *put(const QNetworkRequest &request, const QByteArray &data);
QNetworkReply *put(const QNetworkRequest &request, QHttpMultiPart *multiPart);
QNetworkReply *deleteResource(const QNetworkRequest &request);
QNetworkReply *sendCustomRequest(const QNetworkRequest &request, const QByteArray &verb, QIODevice *data = Q_NULLPTR);
(2)构造请求uri和query
QNetworkRequest request;
QUrlQuery urlQuery;
QString urlString;
QUrl url;
/* url */
urlString.append("http://");
urlString.append("www.baidu.com");
urlString.append("/echo");
url.setUrl(urlString);
/* query */
urlQuery.addQueryItem("id", "123456");
urlQuery.addQueryItem("time", "2019-01-01 10:30:00");
url.setQuery(urlQuery);
request.setUrl(url);
(3)追加/修改请求头
QNetworkRequest request;
request.setRawHeader("User-Agent", "qhttpc 1.0");
request.setRawHeader("Authorization", "Basic YWRtaW46YWRtaW4=");
(3)追加请求body
在使用post或put方式的请求时,需要追加请求负载数据时,将负载数据带入请求
manager->post(request, data);
(1)接收回复状态码
QVariant statusCode = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute);
if (statusCode.isValid()) {
qDebug() << "recv finished: " << statusCode.toInt();
}
int httpCode = statusCode.toInt();
(2)接收回复头
QList<QByteArray> list = reply->rawHeaderList();
for (int i = 0; i < list.size(); i++) {
qDebug() << "header: " << list.at(i).data() << " " << reply->rawHeader(list.at(i));
}
(3)接收回复数据
response = reply->readAll();
增加定时器,设置超时时间为30秒,当reply接收完成或超时后,eventLoop.exec()执行退出,继续处理数据处理操作
QEventLoop eventLoop;
QTimer timer;
connect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
connect(&timer, SIGNAL(timeout()), &eventLoop, SLOT(quit()));
timer.start(30 * 1000);
eventLoop.exec(); // block until finish
if (timer.isActive()) {
timer.stop();
} else {
disconnect(reply, SIGNAL(finished()), &eventLoop, SLOT(quit()));
reply->abort();
reply->deleteLater();
return -1;
}
http请求的认证信息通过增加认证头来实现。用户名和密码常用的加密方式有basic和digest加密
Basic加密头格式:
Authorization: Basic base64(username:password)
Digest加密头格式:
Authorization: Digest MD5(username:realm:password)
如果认证方式选择Basic方式,并且输入了用户名和密码,工具中实现将追加Authorization,尝试发起附带认证头的请求。无法追加Digest认证头,是因为需要根据服务端返回的数据对用户名和密码进行加密。
Qt中将用户认证的过程封装在内部实现,在需要认证时只需输入用户名和密码,QNetworkAccessManager类会自动生成认证头。使用方式如下:
通过QNetworkAccessManager类设置认证请求的槽函数,在槽函数中将用户的认证信息设置生效
connect(manager, SIGNAL(authenticationRequired(QNetworkReply*,QAuthenticator*)),
this, SLOT(authRequiredReply(QNetworkReply*,QAuthenticator*)));
当服务端返回了401的错误码后,会触发authenticationRequire
信号,信号处理函数如下所示:
void Widget::authRequiredReply(QNetworkReply *reply, QAuthenticator *auth)
{
QString username = ui->usernameLineEdit->text();
QString password = ui->passwordLineEdit->text();
qDebug() << "username: " << username << "password: " << password;
if (username.isEmpty() || password.isEmpty()) {
QMessageBox::information(this, tr("Error"), tr("Please input valid username and passowrd!"));
return ;
}
auth->setUser(username);
auth->setPassword(password);
}
主界面主要包含三部分:URL组件、request信息组件、reply信息显示组件
当输入了有效的URL后,点击发送,会自动发起http请求。可以通过Method的combox框选择请求方式;需要在URL后追加query信息是,在发送tab的Query界面进行key和value的追加;需要发起PUT或POST请求时,通过发送tab的Body界面输入需要追加的请求数据。
如果服务端成功返回了数据,返回头显示在是接收tab的Headers界面,返回body显示在是接收tab的Body界面。