319 lines
8.5 KiB
C++
319 lines
8.5 KiB
C++
|
|
#include "httpRequestManager.h"
|
||
|
|
#include <QJsonObject>
|
||
|
|
#include <QJsonArray>
|
||
|
|
#include <QDebug>
|
||
|
|
|
||
|
|
HttpRequestManager::HttpRequestManager(QObject* parent)
|
||
|
|
: QObject(parent)
|
||
|
|
{
|
||
|
|
}
|
||
|
|
HttpRequestManager::~HttpRequestManager()
|
||
|
|
{
|
||
|
|
for(auto& config : m_endpoints)
|
||
|
|
{
|
||
|
|
if(config.activeReply)
|
||
|
|
{
|
||
|
|
config.activeReply->abort();
|
||
|
|
config.activeReply->deleteLater();
|
||
|
|
config.activeReply = nullptr;
|
||
|
|
}
|
||
|
|
if(config.timeoutTimer)
|
||
|
|
{
|
||
|
|
config.timeoutTimer->stop();
|
||
|
|
config.timeoutTimer->deleteLater();
|
||
|
|
config.timeoutTimer = nullptr;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::sendRequest(EndpointConfig& config)
|
||
|
|
{
|
||
|
|
QNetworkRequest request(config.url);
|
||
|
|
|
||
|
|
for(auto it = config.headers.constBegin(); it != config.headers.constEnd(); ++it)
|
||
|
|
request.setRawHeader(it.key(), it.value());
|
||
|
|
|
||
|
|
QNetworkReply* reply = nullptr;
|
||
|
|
if(config.method == "GET")
|
||
|
|
reply = m_networkManager.get(request);
|
||
|
|
else if(config.method == "POST")
|
||
|
|
reply = m_networkManager.post(request, config.body);
|
||
|
|
else if(config.method == "PUT")
|
||
|
|
reply = m_networkManager.put(request, config.body);
|
||
|
|
else if(config.method == "DELETE")
|
||
|
|
reply = m_networkManager.deleteResource(request);
|
||
|
|
else
|
||
|
|
{
|
||
|
|
qWarning() << "Unsupported HTTP method:" << config.method;
|
||
|
|
emit requestFailed(config.dataKey, "Unsupported HTTP method");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
connect(reply, &QNetworkReply::finished, this, &HttpRequestManager::onReplyFinished);
|
||
|
|
connect(reply, &QNetworkReply::errorOccurred, this, &HttpRequestManager::onReplyError);
|
||
|
|
|
||
|
|
config.timeoutTimer = new QTimer(this);
|
||
|
|
config.timeoutTimer->setSingleShot(true);
|
||
|
|
config.timeoutTimer->setInterval(m_defaultTimeout);
|
||
|
|
connect(config.timeoutTimer, &QTimer::timeout, this, [this, &config](){
|
||
|
|
handleRequestTimeout(config.dataKey);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::handleResponse(const QString& dataKey, QNetworkReply* reply)
|
||
|
|
{
|
||
|
|
if(!m_endpoints.contains(dataKey))
|
||
|
|
{
|
||
|
|
qWarning() << "Recived response for unregistered dataKey:" << dataKey;
|
||
|
|
reply->deleteLater();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
EndpointConfig& config = m_endpoints[dataKey];
|
||
|
|
//关闭超时重连定时器
|
||
|
|
if(config.timeoutTimer)
|
||
|
|
{
|
||
|
|
config.timeoutTimer->stop();
|
||
|
|
config.timeoutTimer->deleteLater();
|
||
|
|
config.timeoutTimer = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(reply->error() != QNetworkReply::NoError)
|
||
|
|
return; //具体错误信息在onReplyError中进行处理
|
||
|
|
|
||
|
|
QByteArray responseData = reply->readAll();
|
||
|
|
QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString();
|
||
|
|
if(contentType.contains("application/json"))
|
||
|
|
parseJsonResponse(dataKey, responseData);
|
||
|
|
else if(contentType.contains("text/"))
|
||
|
|
parseTextResponse(dataKey, responseData);
|
||
|
|
else
|
||
|
|
parseBinaryResponse(dataKey, responseData);
|
||
|
|
|
||
|
|
//数据重置
|
||
|
|
reply->deleteLater();
|
||
|
|
config.retryCount = 0;
|
||
|
|
config.activeReply = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::handleRequestTimeout(const QString& dataKey)
|
||
|
|
{
|
||
|
|
if(!m_endpoints.contains(dataKey))
|
||
|
|
return;
|
||
|
|
|
||
|
|
EndpointConfig& config = m_endpoints[dataKey];
|
||
|
|
qWarning() << "Request timeout for dataKey: " << dataKey;
|
||
|
|
if(config.activeReply)
|
||
|
|
config.activeReply->abort();
|
||
|
|
//重试
|
||
|
|
if(config.retryCount < config.maxRetries)
|
||
|
|
scheduleRetry(config.dataKey);
|
||
|
|
else
|
||
|
|
{
|
||
|
|
emit requestFailed(config.dataKey, "Request timed out");
|
||
|
|
config.retryCount = 0;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::scheduleRetry(const QString& dataKey)
|
||
|
|
{
|
||
|
|
if(!m_endpoints.contains(dataKey))
|
||
|
|
return;
|
||
|
|
|
||
|
|
EndpointConfig& config = m_endpoints[dataKey];
|
||
|
|
config.retryCount++;
|
||
|
|
|
||
|
|
QTimer::singleShot(config.retryInterval, this, [this, dataKey](){
|
||
|
|
requestData(dataKey);
|
||
|
|
});
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::parseJsonResponse(const QString& dataKey, const QByteArray& data)
|
||
|
|
{
|
||
|
|
QJsonParseError parseError;
|
||
|
|
QJsonDocument document = QJsonDocument::fromJson(data, &parseError);
|
||
|
|
if(document.isNull())
|
||
|
|
{
|
||
|
|
emit requestFailed(dataKey, "get JSonDocument failed");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
if(parseError.error != QJsonParseError::NoError)
|
||
|
|
{
|
||
|
|
emit requestFailed(dataKey, "JSON parse error: " + parseError.errorString());
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
if(document.isObject())
|
||
|
|
emit dataReceived(dataKey, document.object().toVariantMap());
|
||
|
|
else if(document.isArray())
|
||
|
|
emit dataReceived(dataKey, document.array().toVariantList());
|
||
|
|
else
|
||
|
|
emit dataReceived(dataKey, QVariant());
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::parseTextResponse(const QString& dataKey, const QByteArray& data)
|
||
|
|
{
|
||
|
|
emit dataReceived(dataKey, QString::fromUtf8(data));
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::parseBinaryResponse(const QString& dataKey, const QByteArray& data)
|
||
|
|
{
|
||
|
|
emit dataReceived(dataKey, data);
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::registerEndpoint(const QString& dataKey, const QUrl& url, const QByteArray& method,
|
||
|
|
const QByteArray& body, const QMap<QByteArray, QByteArray>& headers)
|
||
|
|
{
|
||
|
|
EndpointConfig config;
|
||
|
|
config.dataKey = dataKey;
|
||
|
|
config.url = url;
|
||
|
|
config.method = method.toUpper();
|
||
|
|
config.body = body;
|
||
|
|
config.headers = headers;
|
||
|
|
config.maxRetries = m_maxRetries;
|
||
|
|
config.retryInterval = m_retryIntrval;
|
||
|
|
|
||
|
|
m_endpoints[dataKey] = config;
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::requestData(const QString& dataKey)
|
||
|
|
{
|
||
|
|
if(!m_endpoints.contains(dataKey))
|
||
|
|
{
|
||
|
|
qWarning() << "This dataKey is not present in endpoints: " << dataKey;
|
||
|
|
emit requestFailed(dataKey, "dataKey is not present in endpoints");
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
EndpointConfig& config = m_endpoints[dataKey];
|
||
|
|
if(config.activeReply)
|
||
|
|
{
|
||
|
|
config.activeReply->abort();
|
||
|
|
config.activeReply->deleteLater();
|
||
|
|
config.activeReply = nullptr;
|
||
|
|
}
|
||
|
|
if(config.timeoutTimer)
|
||
|
|
{
|
||
|
|
config.timeoutTimer->stop();
|
||
|
|
config.timeoutTimer->deleteLater();
|
||
|
|
config.timeoutTimer = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
sendRequest(config);
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::cancleRequest(const QString& dataKey)
|
||
|
|
{
|
||
|
|
if(!m_endpoints.contains(dataKey))
|
||
|
|
return;
|
||
|
|
|
||
|
|
EndpointConfig& config = m_endpoints[dataKey];
|
||
|
|
if(config.activeReply)
|
||
|
|
{
|
||
|
|
config.activeReply->abort();
|
||
|
|
config.activeReply->deleteLater();
|
||
|
|
config.activeReply = nullptr;
|
||
|
|
}
|
||
|
|
if(config.timeoutTimer)
|
||
|
|
{
|
||
|
|
config.timeoutTimer->stop();
|
||
|
|
config.timeoutTimer->deleteLater();
|
||
|
|
config.timeoutTimer = nullptr;
|
||
|
|
}
|
||
|
|
config.retryCount = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::setDefaultTimeout(int ms)
|
||
|
|
{
|
||
|
|
m_defaultTimeout = qMax(1000, ms);
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::setRetryPolicy(int maxRetries, int retryInterval)
|
||
|
|
{
|
||
|
|
m_maxRetries = qMax(0, maxRetries);
|
||
|
|
m_retryIntrval = qMax(500, retryInterval);
|
||
|
|
//更新所以已注册端点的充实策略
|
||
|
|
for(auto& config : m_endpoints)
|
||
|
|
{
|
||
|
|
config.maxRetries = m_maxRetries;
|
||
|
|
config.retryInterval = m_retryIntrval;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool HttpRequestManager::hasVaildEndpoint(const QString& dataKey)
|
||
|
|
{
|
||
|
|
return m_endpoints.contains(dataKey) && m_endpoints[dataKey].url.isEmpty();
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::onReplyFinished()
|
||
|
|
{
|
||
|
|
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||
|
|
if(!reply)
|
||
|
|
return;
|
||
|
|
|
||
|
|
QString dataKey;
|
||
|
|
for(auto& config : m_endpoints)
|
||
|
|
{
|
||
|
|
if(config.activeReply == reply)
|
||
|
|
{
|
||
|
|
dataKey = config.dataKey;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(dataKey.isEmpty())
|
||
|
|
{
|
||
|
|
reply->deleteLater();
|
||
|
|
return;
|
||
|
|
}
|
||
|
|
|
||
|
|
handleResponse(dataKey, reply);
|
||
|
|
}
|
||
|
|
|
||
|
|
void HttpRequestManager::onReplyError(QNetworkReply::NetworkError code)
|
||
|
|
{
|
||
|
|
Q_UNUSED(code);
|
||
|
|
|
||
|
|
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||
|
|
if(!reply)
|
||
|
|
return;
|
||
|
|
|
||
|
|
QString dataKey;
|
||
|
|
for(auto& config : m_endpoints)
|
||
|
|
{
|
||
|
|
if(config.activeReply == reply)
|
||
|
|
{
|
||
|
|
dataKey = config.dataKey;
|
||
|
|
break;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
if(dataKey.isEmpty())
|
||
|
|
return;
|
||
|
|
|
||
|
|
EndpointConfig& config = m_endpoints[dataKey];
|
||
|
|
//关闭超时重连定时器
|
||
|
|
if(config.timeoutTimer)
|
||
|
|
{
|
||
|
|
config.timeoutTimer->stop();
|
||
|
|
config.timeoutTimer->deleteLater();
|
||
|
|
config.timeoutTimer = nullptr;
|
||
|
|
}
|
||
|
|
|
||
|
|
//打印错误
|
||
|
|
QString error = reply->errorString();
|
||
|
|
qWarning() << "HTTP request failed for " << dataKey << ":" << error;
|
||
|
|
|
||
|
|
//重试
|
||
|
|
if(config.retryCount < config.maxRetries)
|
||
|
|
scheduleRetry(dataKey);
|
||
|
|
else
|
||
|
|
{
|
||
|
|
emit requestFailed(dataKey, error);
|
||
|
|
config.retryCount = 0;
|
||
|
|
}
|
||
|
|
|
||
|
|
reply->deleteLater();
|
||
|
|
config.activeReply = nullptr;
|
||
|
|
}
|