前言:
说本次是测试,还不如说是总结,
本次测试的是一些常见的http请求,采用同步的方式实现
一、测试同步get请求
实现代码如下
/*!
* \brief Tool::waitGet 同步get请求 返回json对象
* \param url 请求的url
* \param para 请求的参数
* \param responseJsonObject 返回的json对象
* \param errorString 错误提示串
* \return
*/
bool Tool::waitGet(const QUrl &url, const QVariantMap ¶, QJsonObject &responseJsonObject, QString &errorString)
{
QNetworkRequest req;
QUrlQuery qry(url.query());
QMapIterator<QString, QVariant> i(para);
while (i.hasNext()) {
i.next();
qry.addQueryItem(i.key(), i.value().toString());
}
QUrl finalUrl(url);
finalUrl.setQuery(qry);
req.setUrl(finalUrl);
首先实例化QNetworkRequest,将传入参数作为url的查询参数
如https://localhost:3001/api/v1/users
加入参数后可能变成https://localhost:3001/api/v1/users?arg1=val1&arg2=val2
QNetworkAccessManager netam;
netam.setTransferTimeout(10000);
QNetworkReply *reply = netam.get(req);
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
setTransferTimeout()设置超时时间,需要Qt5.15及以上版本才有,若使用较低版本,需要自己设置一个定时器实现(不明白官方,这么简单的功能,不早点实现)
然后创建一个事件循环,直到数据返回时,再继续向下执行
if (reply->error() != QNetworkReply::NoError) {
errorString = tr("Get请求异常") + reply->errorString();
reply->deleteLater();
return false;
}
检查返回结果是否正常
QJsonParseError jsonParseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &jsonParseError);
reply->deleteLater();
if (jsonParseError.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse text message as JSON object."
<< "Error is:" << jsonParseError.errorString();
errorString = QString("Response Data ERROR: %1").arg(jsonParseError.errorString());
return false;
} else if (!jsonDoc.isObject()) {
qWarning() << "Received JSON message that is not an object";
errorString = tr("返回JSON数据无法解析!");
return false;
}
responseJsonObject = jsonDoc.object();
return true;
}
若返回正常,将返回数据作为json串进行处理,并返回
二、测试同步get文件下载
实现代码如下
/*!
* \brief Tool::waitGet 同步get请求 下载文件
* \param url 请求的文件url
* \param localFilePath 文件下载目录,若不指定,下载到系统下载目录
* \param localFileName 传入时,只是文件名;若下载成功,返回文件全目录
* \param errorString
* \return
*/
bool Tool::waitGet(const QUrl &url, QString &localFilePath,
QString &localFileName, QString &errorString)
{
QNetworkRequest req;
req.setUrl(url);
QNetworkAccessManager netam;
netam.setTransferTimeout(10000);
QNetworkReply *reply = netam.get(req);
connect(&netam, &QNetworkAccessManager::sslErrors, &netam,
[](QNetworkReply *reply, const QList<QSslError> &/*errors*/) {
reply->ignoreSslErrors(); });
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
首先实例化QNetworkRequest,
setTransferTimeout()设置超时时间,需要Qt5.15及以上版本才有,若使用较低版本,需要自己设置一个定时器实现(不明白官方,这么简单的功能,不早点实现)
然后创建一个事件循环,直到数据返回时,再继续向下执行
if (reply->error() != QNetworkReply::NoError) {
errorString = tr("Get请求异常") + reply->errorString();
reply->deleteLater();
return false;
}
检查返回结果是否正常
QFileInfo fileInfo= url.path();
QString filePath;
if(localFilePath.isEmpty()){ //没有指定文件存放路径 ,默认存放在系统下载目录
filePath = QStandardPaths::writableLocation(QStandardPaths::DownloadLocation);;
QDir dir = QDir();
if(!dir.exists(filePath)){
dir.mkpath(filePath);
}
}else{
filePath = localFilePath;
QDir dir = QDir();
if(!dir.exists(filePath)){
dir.mkpath(filePath);
}
}
QString fileUrl;
if(localFileName.isEmpty()){ //没有指定更改文件名,使用默认文件名
fileUrl = filePath + QLatin1String("/") + fileInfo.fileName();
}else{
fileUrl = filePath + QLatin1String("/") + localFileName;
}
若是指定了文件下载目录,就下载到指定目录中;若未指定,就下载到系统下载目录中
若指定了文件名,就使用指定文件名;若未指定文件名,从url中获取
QFile file = QFile(fileUrl);
bool ret = file.open(QIODevice::WriteOnly|QIODevice::Truncate); //创建文件
if(!ret){
errorString = tr("文件打开失败!%1").arg(file.errorString());
return false;
}
QByteArray ba = reply->readAll();
reply->deleteLater();
file.write(ba);
file.close();
qDebug()<<"文件数据写入成功,写入的文件名 " + fileUrl;
localFileName = fileUrl;
return true;
}
将所有的数据直接写入文件中
若是文件太大,不建议使用同步下载,可能会卡住太长时间的
三、测试同步post请求
实现代码如下
/*!
* \brief Tool::waitPost 同步post请求
* \param url 请求的url
* \param requestJsonObject 请求的post对象
* \param responseJsonObject 返回的json对象
* \param errorString 错误信息
* \return
*/
bool Tool::waitPost(const QUrl &url, const QJsonObject &requestJsonObject, QJsonObject &responseJsonObject, QString &errorString)
{
QNetworkRequest req;
req.setUrl(url);
req.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
首先实例化QNetworkRequest,
请求的头要设置正确,否则很多服务器无法正常处理
QNetworkAccessManager netam;
netam.setTransferTimeout(10000);
QNetworkReply *reply = netam.post(req, QJsonDocument(requestJsonObject).toJson(QJsonDocument::Compact));
// 阻塞,等待请求完成
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
setTransferTimeout()设置超时时间,需要Qt5.15及以上版本才有,若使用较低版本,需要自己设置一个定时器实现(不明白官方,这么简单的功能,不早点实现)
然后创建一个事件循环,直到数据返回时,再继续向下执行
if (reply->error() != QNetworkReply::NoError) {
errorString = tr("POST请求异常") + reply->errorString();
reply->deleteLater();
return false;
}
检查返回结果是否正常
QJsonParseError jsonParseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &jsonParseError);
reply->deleteLater();
if (jsonParseError.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse text message as JSON object."
<< "Error is:" << jsonParseError.errorString();
errorString = QString("Response Data ERROR: %1").arg(jsonParseError.errorString());
return false;
} else if (!jsonDoc.isObject()) {
qWarning() << "Received JSON message that is not an object";
errorString = tr("返回JSON数据无法解析!");
return false;
}
responseJsonObject = jsonDoc.object();
return true;
}
若返回正常,将返回数据作为json串进行处理,并返回
四、测试同步post 通过表单实现文件上传
实现代码如下
/*!
* \brief Tool::waitPost
* \param url 请求的文件url
* \param fileName 要上传的文件
* \param responseJsonObject 返回的json对象
* \param errorString 错误信息
* \return
*/
bool Tool::waitPost(const QUrl &url, const QString &fileName, QJsonObject &responseJsonObject, QString &errorString)
{
QFileInfo info(fileName);
if(!info.isFile())
{
errorString = tr("文件路径异常");
return false;
}
先确定下,文件是有效的
//设置multiPart
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QHttpPart filePart;
QString header=QString("form-data; name=\"file\";filename=\"%1\"").arg(info.fileName());
filePart.setHeader(QNetworkRequest::ContentDispositionHeader, QVariant(header));
QFile *file = new QFile(fileName);
file->open(QIODevice::ReadOnly);
filePart.setBodyDevice(file);
file->setParent(multiPart);
// we cannot delete the file now, so delete it with the multiPart
multiPart->append(filePart);
自定义的头部,一定要写对,要影响到服务器的解析;
若无法正常上传文件,可以去服务端看下,处理的参数是否正确
QNetworkRequest req;
req.setUrl(url);
QNetworkAccessManager netam;
netam.setTransferTimeout(10000);
QNetworkReply *reply = netam.post(req, QJsonDocument(requestJsonObject).toJson(QJsonDocument::Compact));
// 阻塞,等待请求完成
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
setTransferTimeout()设置超时时间,需要Qt5.15及以上版本才有,若使用较低版本,需要自己设置一个定时器实现(不明白官方,这么简单的功能,不早点实现)
然后创建一个事件循环,直到数据返回时,再继续向下执行
if (reply->error() != QNetworkReply::NoError) {
errorString = tr("POST请求异常") + reply->errorString();
reply->deleteLater();
return false;
}
检查返回结果是否正常
QJsonParseError jsonParseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &jsonParseError);
reply->deleteLater();
if (jsonParseError.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse text message as JSON object."
<< "Error is:" << jsonParseError.errorString();
errorString = QString("Response Data ERROR: %1").arg(jsonParseError.errorString());
return false;
} else if (!jsonDoc.isObject()) {
qWarning() << "Received JSON message that is not an object";
errorString = tr("返回JSON数据无法解析!");
return false;
}
responseJsonObject = jsonDoc.object();
return true;
}
若返回正常,将返回数据作为json串进行处理,并返回
请求url中,仍然可以放入查询参数的,方便传入一些参数
五、测试同步put请求
很多服务器已经不支持处理put请求了,需要先确定下服务端是否支持
客户端的实现基本与post方法相同,实现代码如下
/*!
* \brief Tool::waitPut 同步put请求
* \param url 请求的url
* \param requestJsonObject 请求的post对象
* \param responseJsonObject 返回的json对象
* \param errorString 错误信息
* \return
*/
bool Tool::waitPut(const QUrl &url, const QJsonObject &requestJsonObject, QJsonObject &responseJsonObject, QString &errorString)
{
QNetworkRequest req;
req.setUrl(url);
req.setHeader(QNetworkRequest::ContentTypeHeader, QVariant("application/json"));
首先实例化QNetworkRequest,
请求的头要设置正确,否则很多服务器无法正常处理
QNetworkAccessManager netam;
netam.setTransferTimeout(10000);
QNetworkReply *reply = netam.put(req, QJsonDocument(requestJsonObject).toJson(QJsonDocument::Compact));
// 阻塞,等待请求完成
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
setTransferTimeout()设置超时时间,需要Qt5.15及以上版本才有,若使用较低版本,需要自己设置一个定时器实现(不明白官方,这么简单的功能,不早点实现)
然后创建一个事件循环,直到数据返回时,再继续向下执行
if (reply->error() != QNetworkReply::NoError) {
errorString = tr("PUT请求异常") + reply->errorString();
reply->deleteLater();
return false;
}
检查返回结果是否正常
QJsonParseError jsonParseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &jsonParseError);
reply->deleteLater();
if (jsonParseError.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse text message as JSON object."
<< "Error is:" << jsonParseError.errorString();
errorString = QString("Response Data ERROR: %1").arg(jsonParseError.errorString());
return false;
} else if (!jsonDoc.isObject()) {
qWarning() << "Received JSON message that is not an object";
errorString = tr("返回JSON数据无法解析!");
return false;
}
responseJsonObject = jsonDoc.object();
return true;
}
若返回正常,将返回数据作为json串进行处理,并返回
六、测试同步delete请求
很多服务器已经不支持处理delete请求了,需要先确定下服务端是否支持
客户端的实现基本与get方法相同,实现代码如下
/*!
* \brief Tool::waitDelete 同步delete请求 返回json对象
* \param url 请求的url
* \param para 请求的参数
* \param responseJsonObject 返回的json对象
* \param errorString 错误提示串
* \return
*/
bool Tool::waitDelete(const QUrl &url, const QVariantMap ¶, QJsonObject &responseJsonObject, QString &errorString)
{
QNetworkRequest req;
QUrlQuery qry(url.query());
QMapIterator<QString, QVariant> i(para);
while (i.hasNext()) {
i.next();
qry.addQueryItem(i.key(), i.value().toString());
}
QUrl finalUrl(url);
finalUrl.setQuery(qry);
req.setUrl(finalUrl);
首先实例化QNetworkRequest,将传入参数作为url的查询参数
如https://localhost:3001/api/v1/users
加入参数后可能变成https://localhost:3001/api/v1/users?arg1=val1&arg2=val2
QNetworkAccessManager netam;
netam.setTransferTimeout(10000);
QNetworkReply *reply = netam.get(req);
QEventLoop eventLoop;
connect(reply, &QNetworkReply::finished, &eventLoop, &QEventLoop::quit);
eventLoop.exec();
setTransferTimeout()设置超时时间,需要Qt5.15及以上版本才有,若使用较低版本,需要自己设置一个定时器实现(不明白官方,这么简单的功能,不早点实现)
然后创建一个事件循环,直到数据返回时,再继续向下执行
if (reply->error() != QNetworkReply::NoError) {
errorString = tr("delete请求异常") + reply->errorString();
reply->deleteLater();
return false;
}
检查返回结果是否正常
QJsonParseError jsonParseError;
QJsonDocument jsonDoc = QJsonDocument::fromJson(reply->readAll(), &jsonParseError);
reply->deleteLater();
if (jsonParseError.error != QJsonParseError::NoError) {
qWarning() << "Failed to parse text message as JSON object."
<< "Error is:" << jsonParseError.errorString();
errorString = QString("Response Data ERROR: %1").arg(jsonParseError.errorString());
return false;
} else if (!jsonDoc.isObject()) {
qWarning() << "Received JSON message that is not an object";
errorString = tr("返回JSON数据无法解析!");
return false;
}
responseJsonObject = jsonDoc.object();
return true;
}
若返回正常,将返回数据作为json串进行处理,并返回
七、增加SSL支持
以上同步操作的实现,都是http请求,现在更多的是https请求,需要在请求对象中,加入如下配置
QNetworkRequest request;
...
#if QT_CONFIG(ssl)
QSslConfiguration ssl;
ssl.setPeerVerifyMode(QSslSocket::VerifyNone);
ssl.setProtocol(QSsl::AnyProtocol);
req.setSslConfiguration(ssl);
#endif
以上是单向证书需要配置的参数,笔者不建议设置全部协议,建议具体设置使用的协议
只设置这些还不够,还需要忽略ssl错误才行
#if QT_CONFIG(ssl)
connect(&netam, &QNetworkAccessManager::sslErrors, &netam,
[](QNetworkReply *reply, const QList<QSslError> &/*errors*/) {
reply->ignoreSslErrors(); });
#endif
若是要设置双向证书,添加如下代码
QNetworkRequest request;
...
#if QT_CONFIG(ssl)
QSslConfiguration ssl;
QList<QSslCertificate> certs = QSslCertificate::fromPath("C:\\certificate.crt");
ssl.setCaCertificates(certs);
request.setSslConfiguration(ssl);
#endif
示例代码下载
后记:
Qt本身的http是异步的,记得一个QNetworkAccessManager会创建6个连接,所以同一个对象,不要同时超过6个请求
笔者建议,若非必要,还是使用异步的方式通信,不建议使用同步的方式