我当前使用的Qt版本是5.5.1 该版本的HTTP相关处理模块在遇到重定向的时候不会自动重定向,需要手工处理,我在这个例子中处理了这种方式。
话不多说我直接上示例代码了:
类名:CHttpSupport
.h文件
#ifndef CHTTPSUPPORT_H
#define CHTTPSUPPORT_H
#include <QNetworkAccessManager>
class CHttpSupport: public QObject
{
Q_OBJECT
private:
CHttpSupport(QObject* parent = NULL);
~CHttpSupport();
private:
//////
static CHttpSupport* gInstance;
//////
//////
QNetworkAccessManager* mNetAccessManager;
QString mCacheRoot; //* 缓存目录
QMap<QString, bool> mProcessingRq; //* 当前正在处理的请求url 和是否正在处理
QMap<QString, QByteArray> mDownloadDataCache; //* 数据缓存url -> data | postMD5 -> data
QMap<QString, QString> mRedirectMap; //* 重定向关系
//////
signals:
///
/// \brief httpGetRspReady http get 请求得到回复
/// \param url 请求的地址
/// \param data 回复的数据(如果isEmpty则表示请求出错了)
///
void httpGetRspReady(QString url, QByteArray data);
///
/// \brief httpPostRspReady http post 请求得到回复
/// \param url 请求的地址
/// \param postMD5 是post的时候url+数据的md5
/// \param data data是回复的数据(如果isEmpty则表示请求出错了)
///
void httpPostRspReady(QString url, QString postMD5, QByteArray data);
private slots:
void clearRp(QNetworkReply* rp);
void onHttpGetRspProgress(qint64 bytesReceived, qint64 bytesTotal); //* http get 回复进度
void onHttpGetRspFinished(); //* http get 处理完毕
void onHttpPostRspProgress(qint64 bytesReceived, qint64 bytesTotal); //* http post 回复进度
void onHttpPostRspFinished(); //* http get 处理完毕
public:
/////
static CHttpSupport* instance ();
static bool hasInstance ();
static void releaseInstance();
/////
/////
/// \brief httpGet 发起一个http get 请求
/// \param url 请求地址
///
void httpGet(const QString& url);
///
/// \brief httpPost 发起一个http post 请求
/// \param url 是请求的链接
/// \param data 是传送的数据
/// \return 返回url+data的md5
///
QByteArray httpPost(const QString& url, const QByteArray &data);
};
#endif // CHTTPSUPPORT_H
接下来是.cpp文件
#include "CHttpSupport.h"
#include <QCryptographicHash>
#include <QStandardPaths>
#include <QFile>
#include <QNetworkRequest>
#include <QNetworkReply>
#include <QImage>
#include <QDir>
CHttpSupport* CHttpSupport::gInstance = 0;
CHttpSupport::CHttpSupport(QObject *parent)
:QObject(parent)
,mNetAccessManager(new QNetworkAccessManager(this))
{
}
CHttpSupport::~CHttpSupport()
{
}
void CHttpSupport::clearRp(QNetworkReply *rp)
{
if(rp)
{
QString url = rp->request().url().toString();
QString postMD5 = rp->property("postMD5").toString();
QString postData = rp->property("data").toByteArray();
if(postMD5.isEmpty())
{
//清理对应缓存
mDownloadDataCache.remove(url);
//解除正在处理状态
mProcessingRq.remove(url);
}
else
{
//清理对应缓存
mDownloadDataCache.remove(postMD5);
//解除正在处理状态
mProcessingRq.remove(postMD5);
}
mRedirectMap.remove(url);
mRedirectMap.remove(postMD5);
qDebug() << "delete cache, url:" << url << " postMOD5:" << postMD5;
rp->deleteLater();
}
}
void CHttpSupport::onHttpGetRspProgress(qint64 bytesReceived, qint64 bytesTotal)
{
if(sender() == NULL)
{
return ;
}
QNetworkReply* rp = qobject_cast<QNetworkReply*>(sender());
if(rp == NULL)
{
return;
}
qDebug() << "http get rsp progress:" << rp->url().toString() << bytesReceived << "/" << bytesTotal;
if(bytesTotal <= 0)
{
return;
}
QString url = rp->url().toString();
mDownloadDataCache[url].append(rp->readAll());
}
void CHttpSupport::onHttpGetRspFinished()
{
if(sender() == NULL)
{
return ;
}
QNetworkReply* rp = qobject_cast<QNetworkReply*>(sender());
if(rp == NULL)
{
return;
}
QByteArray rpData;
QString url = rp->url().toString();
int statusCode = rp->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString strUrl = rp->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
switch(statusCode)
{
case 200: // ok
{
rpData = mDownloadDataCache[rp->url().toString()];
QString redirectUrl = mRedirectMap[url];
if(redirectUrl.isEmpty())
{
qDebug() << "http get rsp ok:" << url << "total size:" << rpData.count() << "bytes";
emit httpGetRspReady(url, rpData);
}
else
{
qDebug() << "http get rsp ok:" << url << "total size:" << rpData.count() << "bytes" << "[redirect by]" << redirectUrl;
emit httpGetRspReady(redirectUrl, rpData);
}
}
break;
case 301:
case 302: // redirect
{
if(!strUrl.isEmpty())
{
QString turl = mRedirectMap[url];
if(turl.isEmpty())
mRedirectMap[strUrl] = url;
else
mRedirectMap[strUrl] = turl;
httpGet(strUrl);
}
}
break;
default: // error
{
qDebug() << url << "[get error:" << statusCode << "]";
QString redirectUrl = mRedirectMap[url];
if(redirectUrl.isEmpty())
{
emit httpGetRspReady(url, QByteArray());
}
else
{
emit httpGetRspReady(redirectUrl, QByteArray());
}
}
break;
}
clearRp(rp);
}
void CHttpSupport::onHttpPostRspProgress(qint64 bytesReceived, qint64 bytesTotal)
{
if(sender() == NULL)
{
return ;
}
QNetworkReply* rp = qobject_cast<QNetworkReply*>(sender());
if(rp == NULL)
{
return;
}
qDebug() << "http post rsp progress:" << rp->url().toString() << bytesReceived << "/" << bytesTotal;
if(bytesTotal <= 0)
{
return;
}
QString postMD5 = rp->property("postMD5").toString();
mDownloadDataCache[postMD5].append(rp->readAll());
}
void CHttpSupport::onHttpPostRspFinished()
{
if(sender() == NULL)
{
return ;
}
QNetworkReply* rp = qobject_cast<QNetworkReply*>(sender());
if(rp == NULL)
{
return;
}
QByteArray rpData;
QString url = rp->url().toString();
QString postMD5 = rp->property("postMD5").toString();
QByteArray postData = rp->property("data").toByteArray();
int statusCode = rp->attribute(QNetworkRequest::HttpStatusCodeAttribute).toInt();
QString strUrl = rp->attribute(QNetworkRequest::RedirectionTargetAttribute).toString();
switch(statusCode)
{
case 200:
{
rpData = mDownloadDataCache[postMD5];
QString redirectMD5 = mRedirectMap[postMD5];
QString redirectUrl = mRedirectMap[url];
if(redirectMD5.isEmpty() || redirectUrl.isEmpty())
{
qDebug() << "http post rsp ok:" << url << "total size:" << rpData.count() << "bytes";
emit httpPostRspReady(url, postMD5, rpData);
}
else
{
qDebug() << "http post rsp ok:" << url << "total size:" << rpData.count() << "bytes" << "[redirect by]" << redirectUrl << "[data]" << postData;
emit httpPostRspReady(redirectUrl, redirectMD5, rpData);
}
}
break;
case 301:
case 302:
{
if(!strUrl.isEmpty())
{
QString turl = mRedirectMap[url];//direct by
if(turl.isEmpty())
mRedirectMap[strUrl] = url;
else
mRedirectMap[strUrl] = turl;
QByteArray d;
d.append(strUrl);
d.append(postData);
QString md5 = QCryptographicHash::hash(d, QCryptographicHash::Md5);
QString tPostMD5 = mRedirectMap[md5];//direct by
if(tPostMD5.isEmpty())
{
mRedirectMap[md5] = postMD5;
}
else
{
mRedirectMap[md5] = tPostMD5;
}
httpPost(strUrl, postData);
}
}
break;
default:
qDebug() << url << "[post error:" << statusCode << "]";
QString redirectMD5 = mRedirectMap[postMD5];
QString redirectUrl = mRedirectMap[url];
if(redirectMD5.isEmpty() || redirectUrl.isEmpty())
{
emit httpPostRspReady(url, postMD5, QByteArray());
}
else
{
emit httpPostRspReady(redirectUrl, redirectMD5, QByteArray());
}
break;
}
clearRp(rp);
}
CHttpSupport *CHttpSupport::instance()
{
if(!gInstance)
gInstance = new CHttpSupport;
return gInstance;
}
bool CHttpSupport::hasInstance()
{
return gInstance != NULL;
}
void CHttpSupport::releaseInstance()
{
if(gInstance)
delete gInstance;
gInstance = NULL;
}
void CHttpSupport::httpGet(const QString &url)
{
if(mProcessingRq.value(url, false)) //ignore when rq processing
{
return;
}
for(int i = 0; i < mRedirectMap.values().count(); i++)
{
if(mRedirectMap.values()[i] == url) //ignore when redirect processing
{
return;
}
}
mProcessingRq.insert(url, true);
QNetworkRequest rq;
QSslConfiguration config = rq.sslConfiguration();
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1SslV3);
rq.setSslConfiguration(config);
rq.setUrl(QUrl(url));
QNetworkReply* rp = mNetAccessManager->get(rq);
connect(rp, &QNetworkReply::finished , this, &CHttpSupport::onHttpGetRspFinished);
connect(rp, &QNetworkReply::downloadProgress, this, &CHttpSupport::onHttpGetRspProgress);
}
QByteArray CHttpSupport::httpPost(const QString &url, const QByteArray &data)
{
QByteArray d;
d.append(url);
d.append(data);
QByteArray md5 = QCryptographicHash::hash(d, QCryptographicHash::Md5);
if(mProcessingRq.value(md5, false)) //ignore when rq processing
{
return md5;
}
for(int i = 0; i < mRedirectMap.values().count(); i++)
{
if(mRedirectMap.values()[i] == md5) //ignore when redirect processing
{
return md5;
}
}
mProcessingRq.insert(md5, true);
QNetworkRequest rq;
QSslConfiguration config = rq.sslConfiguration();
config.setPeerVerifyMode(QSslSocket::VerifyNone);
config.setProtocol(QSsl::TlsV1SslV3);
rq.setSslConfiguration(config);
rq.setUrl(QUrl(url));
QNetworkReply* rp = mNetAccessManager->post(rq, data);
rp->setProperty("postMD5", md5);
rp->setProperty("url", url);
rp->setProperty("data", data);
connect(rp, &QNetworkReply::finished , this, &CHttpSupport::onHttpPostRspFinished);
connect(rp, &QNetworkReply::downloadProgress, this, &CHttpSupport::onHttpPostRspProgress);
return md5;
}
这是一个单例类,通过方法httpGet 和 httpPost可以发送http请求, 当收到请求的时候会通过对应的信号返回结果。
如果请求出错则数据是空的。
使用示例:
int main(int argc, char *argv[])
{
QObject::connect(CHttpSupport::instance(), &CHttpSupport::httpGetRspReady, [](QString url, QByteArray d){
if(url == "http://vip.5211game.com/")
{
qDebug() << "GET";
qDebug() << d;
}
});
CHttpSupport::instance()->httpGet("http://vip.5211game.com/");
return 0;
}
运行结果:
处理成功,得到网页数据,并且这个地址重定向为https://vip.5211game.com/
在这种情况下http://vip.5211game.com/请求的结果返回信号的url为http://vip.5211game.com/ 而data为https://vip.5211game.com/的数据。
(PS:最新版本的Qt已经自动处理重定向了。)
用这个类下载一些小图标,小配置文件什么的很方便。
欢迎大家进行交流和改进。