| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612 |
- #include "networkaccessmanager.h"
- #include "../config/networkconfig.h"
- #include "appevent.h"
- #include <QBuffer>
- #include <QCoreApplication>
- #include <QEventLoop>
- #include <QFile>
- #include <QJsonDocument>
- #include <QJsonObject>
- #include <QMetaObject>
- #include <QUrlQuery>
- #include <QtConcurrent>
- #include "QPromise"
- static const QString messageVal = "msg";
- static NetworkAccessManager* namInstance = nullptr;
- void cleanupNetworkAccessManager()
- {
- delete namInstance;
- namInstance = nullptr;
- }
- NetworkAccessManager* NetworkAccessManager::instance()
- {
- if (!namInstance) {
- namInstance = new NetworkAccessManager;
- qAddPostRoutine(cleanupNetworkAccessManager);
- }
- return namInstance;
- }
- NetworkAccessManager::NetworkAccessManager(QObject* parent)
- : QNetworkAccessManager(parent)
- {
- // #ifdef QT_DEBUG
- // connect(this, &QNetworkAccessManager::finished, this, [](QNetworkReply* reply) {
- // const auto data = reply->readAll();
- // reply->seek(0);
- // const auto startTimeValue = reply->property("startTime");
- // const QDateTime startTime = startTimeValue.value<QDateTime>();
- // const QDateTime endTime = QDateTime::currentDateTime();
- // qDebug() << QString("[%1] time:[%2]ms --> %3")
- // .arg(endTime.toString("yyyy-MM-dd hh:mm:ss.zzz"))
- // .arg((endTime.msecsTo(startTime)))
- // .arg(QString::fromUtf8(data));
- // });
- // #endif
- }
- QNetworkReply* NetworkAccessManager::createRequest(Operation op,
- const QNetworkRequest& request,
- QIODevice* outgoingData)
- {
- QNetworkRequest req(request);
- // 记录请求开始时间
- QDateTime startTime = QDateTime::currentDateTime();
- // 获取操作类型字符串
- QString opStr;
- switch (op) {
- case QNetworkAccessManager::GetOperation:
- opStr = "GET";
- break;
- case QNetworkAccessManager::PostOperation:
- opStr = "POST";
- break;
- case QNetworkAccessManager::PutOperation:
- opStr = "PUT";
- break;
- case QNetworkAccessManager::DeleteOperation:
- opStr = "DELETE";
- break;
- case QNetworkAccessManager::HeadOperation:
- opStr = "HEAD";
- break;
- default:
- opStr = "UNKNOWN";
- break;
- }
- qDebug() << QString("[%1] --> %2 %3")
- .arg(startTime.toString("yyyy-MM-dd hh:mm:ss.zzz"))
- .arg(opStr)
- .arg(request.url().toString());
- // 默认的请求处理
- QString agentStr = QString::fromLatin1("%1/%2 (QNetworkAccessManager %3; %4; %5; %6 bit)")
- .arg(QCoreApplication::applicationName(),
- QCoreApplication::applicationVersion(),
- QLatin1String(qVersion()),
- QSysInfo::prettyProductName(),
- QLocale::system().name())
- .arg(QSysInfo::WordSize);
- req.setRawHeader("User-Agent", agentStr.toLatin1());
- const QString token = AppEvent::instance()->jwtToken();
- if (!token.isEmpty()) {
- req.setRawHeader("Authorization", "Bearer " + token.toUtf8());
- }
- req.setRawHeader("Machine-Code", AppEvent::instance()->machineCode().toUtf8());
- req.setRawHeader("Accept-Language", AppEvent::instance()->locale().toUtf8());
- const QUrl& url = request.url();
- const QString urlPath = url.path();
- if (!urlPath.contains("/api/auth/refresh-token") && !urlPath.contains("/api/auth/login")) {
- if (AppEvent::instance()->isRefreshToken()) {
- emit AppEvent::instance()->refreshTokenNeeded();
- }
- }
- QNetworkReply* reply
- = QNetworkAccessManager::createRequest(op,
- req,
- outgoingData); // 存储开始时间,用于计算请求耗时
- reply->setProperty("startTime", startTime);
- return reply;
- }
- namespace TC {
- RequestClient::RequestClient(const RequestClientOptions& options)
- : m_baseUrl(options.baseURL)
- , m_timeout(options.timeout)
- , m_networkManager(NetworkAccessManager::instance())
- {
- if (options.enableDefaultInterceptors) {
- setupDefaultInterceptors();
- }
- }
- RequestClient::Ptr RequestClient::create(const QString& baseURL, const RequestClientOptions& options)
- {
- RequestClientOptions opts = options;
- opts.baseURL = baseURL;
- return Ptr(new RequestClient(opts));
- }
- RequestClient::Ptr RequestClient::globalInstance()
- {
- static RequestClient::Ptr instance = create(NetworkConfigInstance.getApiBaseUrl(), {"", true});
- return instance;
- }
- RequestClient::Ptr RequestClient::baseGlobalInstance()
- {
- static RequestClient::Ptr instance = create(NetworkConfigInstance.getApiBaseUrl(), {"", false});
- return instance;
- }
- void RequestClient::setupDefaultInterceptors()
- {
- QMutexLocker locker(&m_interceptorMutex);
- // 添加认证拦截器
- m_interceptors.push_back(std::make_shared<AuthRequestInterceptor>());
- // 添加错误处理拦截器
- m_interceptors.push_back(std::make_shared<ErrorResponseInterceptor>());
- }
- void RequestClient::addInterceptor(std::shared_ptr<Interceptor> interceptor)
- {
- QMutexLocker locker(&m_interceptorMutex);
- if (!m_interceptors.contains(interceptor)) {
- m_interceptors.append(interceptor);
- }
- }
- void RequestClient::removeInterceptor(std::shared_ptr<Interceptor> interceptor)
- {
- QMutexLocker locker(&m_interceptorMutex);
- m_interceptors.removeOne(interceptor);
- }
- void RequestClient::clearInterceptors()
- {
- QMutexLocker locker(&m_interceptorMutex);
- m_interceptors.clear();
- }
- HttpResponse RequestClient::get(const QString& url, const QVariantMap& params)
- {
- QScopedPointer<QNetworkReply> reply(sendGetRequest(url, params));
- QEventLoop loop;
- QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
- loop.exec();
- return parseReplyData(reply.get());
- }
- HttpResponse RequestClient::post(const QString& url, const QJsonDocument& data)
- {
- QScopedPointer<QNetworkReply> reply(sendPostRequest(url, data));
- QEventLoop loop;
- QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
- loop.exec();
- return parseReplyData(reply.get());
- }
- HttpResponse RequestClient::put(const QString& url, const QJsonDocument& data)
- {
- QScopedPointer<QNetworkReply> reply(sendPutRequest(url, data));
- QEventLoop loop;
- QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
- loop.exec();
- return parseReplyData(reply.get());
- }
- HttpResponse RequestClient::deleteResource(const QString& url)
- {
- QScopedPointer<QNetworkReply> reply(sendDeleteRequest(url));
- QEventLoop loop;
- QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
- loop.exec();
- return parseReplyData(reply.get());
- }
- HttpResponse RequestClient::upload(const QString& url, QHttpMultiPart* multiPart)
- {
- QScopedPointer<QNetworkReply> reply(sendUploadRequest(url, multiPart));
- QEventLoop loop;
- QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
- loop.exec();
- return parseReplyData(reply.get());
- }
- QFuture<HttpResponse> RequestClient::getAsync(const QString& url, const QVariantMap& params)
- {
- auto promise = std::make_shared<QPromise<HttpResponse>>();
- auto future = promise->future();
- QMetaObject::invokeMethod(
- QCoreApplication::instance(),
- [this, url, params, promise]() {
- try {
- auto result = get(url, params);
- promise->addResult(result);
- promise->finish();
- } catch (...) {
- promise->finish();
- }
- },
- Qt::QueuedConnection);
- return future;
- }
- QFuture<HttpResponse> RequestClient::postAsync(const QString& url, const QJsonDocument& data)
- {
- auto promise = std::make_shared<QPromise<HttpResponse>>();
- auto future = promise->future();
- QMetaObject::invokeMethod(
- QCoreApplication::instance(),
- [this, url, data, promise]() {
- try {
- auto result = post(url, data);
- promise->addResult(result);
- promise->finish();
- } catch (...) {
- promise->finish();
- }
- },
- Qt::QueuedConnection);
- return future;
- }
- QFuture<HttpResponse> RequestClient::putAsync(const QString& url, const QJsonDocument& data)
- {
- auto promise = std::make_shared<QPromise<HttpResponse>>();
- auto future = promise->future();
- QMetaObject::invokeMethod(
- QCoreApplication::instance(),
- [this, url, data, promise]() {
- try {
- auto result = put(url, data);
- promise->addResult(result);
- promise->finish();
- } catch (...) {
- promise->finish();
- }
- },
- Qt::QueuedConnection);
- return future;
- }
- QFuture<HttpResponse> RequestClient::deleteAsync(const QString& url)
- {
- auto promise = std::make_shared<QPromise<HttpResponse>>();
- auto future = promise->future();
- QMetaObject::invokeMethod(
- QCoreApplication::instance(),
- [this, url, promise]() {
- try {
- auto result = deleteResource(url);
- promise->addResult(result);
- promise->finish();
- } catch (...) {
- promise->finish();
- }
- },
- Qt::QueuedConnection);
- return future;
- }
- QFuture<HttpResponse> RequestClient::uploadAsync(const QString& url, QHttpMultiPart* multiPart)
- {
- auto promise = std::make_shared<QPromise<HttpResponse>>();
- auto future = promise->future();
- QMetaObject::invokeMethod(
- QCoreApplication::instance(),
- [this, url, multiPart, promise]() {
- try {
- auto result = upload(url, multiPart);
- promise->addResult(result);
- promise->finish();
- } catch (...) {
- promise->finish();
- }
- },
- Qt::QueuedConnection);
- return future;
- }
- bool RequestClient::download(const QString& url, const QString& saveFilePath)
- {
- QScopedPointer<QNetworkReply> reply(sendGetRequest(url, {}));
- QFile file(saveFilePath);
- if (!file.open(QIODevice::WriteOnly)) {
- return false;
- }
- QEventLoop loop;
- QObject::connect(reply.get(), &QNetworkReply::readyRead, [&]() {
- file.write(reply->readAll());
- });
- QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
- loop.exec();
- file.close();
- return reply->error() == QNetworkReply::NoError;
- }
- QFuture<bool> RequestClient::downloadAsync(const QString& url, const QString& saveFilePath)
- {
- auto promise = std::make_shared<QPromise<bool>>();
- auto future = promise->future();
- QMetaObject::invokeMethod(
- QCoreApplication::instance(),
- [this, url, saveFilePath, promise]() {
- try {
- auto result = download(url, saveFilePath);
- promise->addResult(result);
- promise->finish();
- } catch (...) {
- promise->finish();
- }
- },
- Qt::QueuedConnection);
- return future;
- }
- QNetworkReply* RequestClient::sendGetRequest(const QString& url, const QVariantMap& params)
- {
- m_networkManager->setTransferTimeout(m_timeout);
- QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
- QUrl fullUrl(base + url);
- if (!params.isEmpty()) {
- QUrlQuery query;
- for (auto it = params.constBegin(); it != params.constEnd(); ++it) {
- query.addQueryItem(it.key(), it.value().toString());
- }
- fullUrl.setQuery(query);
- }
- QNetworkRequest request(fullUrl);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- processRequest(request);
- emit requestStarted(fullUrl);
- return m_networkManager->get(request);
- }
- QNetworkReply* RequestClient::sendPostRequest(const QString& url, const QJsonDocument& data)
- {
- m_networkManager->setTransferTimeout(m_timeout);
- QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
- QUrl fullUrl(base + url);
- QBuffer* buffer = new QBuffer;
- buffer->setData(data.toJson());
- buffer->open(QIODevice::ReadOnly);
- QNetworkRequest request(fullUrl);
- request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
- processRequest(request);
- emit requestStarted(fullUrl);
- QNetworkReply* reply = m_networkManager->post(request, buffer);
- buffer->setParent(reply);
- return reply;
- }
- QNetworkReply* RequestClient::sendPutRequest(const QString& url, const QJsonDocument& data)
- {
- m_networkManager->setTransferTimeout(m_timeout);
- QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
- QUrl fullUrl(base + url);
- QBuffer* buffer = new QBuffer;
- buffer->setData(data.toJson());
- buffer->open(QIODevice::ReadOnly);
- QNetworkRequest request(fullUrl);
- processRequest(request);
- emit requestStarted(fullUrl);
- QNetworkReply* reply = m_networkManager->put(request, buffer);
- buffer->setParent(reply);
- return reply;
- }
- QNetworkReply* RequestClient::sendDeleteRequest(const QString& url)
- {
- m_networkManager->setTransferTimeout(m_timeout);
- QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
- QUrl fullUrl(base + url);
- QNetworkRequest request(fullUrl);
- processRequest(request);
- emit requestStarted(fullUrl);
- return m_networkManager->deleteResource(request);
- }
- QNetworkReply* RequestClient::sendUploadRequest(const QString& url, QHttpMultiPart* multiPart)
- {
- m_networkManager->setTransferTimeout(m_timeout);
- QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
- QUrl fullUrl(base + url);
- QNetworkRequest request(fullUrl);
- processRequest(request);
- emit requestStarted(fullUrl);
- return m_networkManager->post(request, multiPart);
- }
- HttpResponse RequestClient::parseReplyData(QNetworkReply* reply)
- {
- HttpResponse response;
- if (reply->error() != QNetworkReply::NoError) {
- response.code = reply->error();
- response.message = reply->errorString();
- emit requestFailed(reply->url(), response.message);
- return response;
- }
- const QByteArray responseData = readReplyData(reply);
- response.rawData = responseData;
- QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
- if (jsonDoc.isObject()) {
- const QJsonObject jsonObj = jsonDoc.object();
- if (jsonObj.contains("code")) {
- response.code = jsonObj["code"].toInt();
- }
- if (jsonObj.contains(messageVal)) {
- response.message = jsonObj[messageVal].toString();
- }
- if (jsonObj.contains("data")) {
- response.data = jsonObj["data"];
- }
- }
- emit requestFinished(reply->url(), response);
- return response;
- }
- void RequestClient::processRequest(QNetworkRequest& request)
- {
- QMutexLocker locker(&m_interceptorMutex);
- for (auto& interceptor : m_interceptors) {
- interceptor->interceptRequest(request);
- }
- }
- void RequestClient::processResponse(QNetworkReply* reply)
- {
- QMutexLocker locker(&m_interceptorMutex);
- for (auto& interceptor : m_interceptors) {
- interceptor->interceptResponse(reply);
- }
- }
- QByteArray RequestClient::readReplyData(QNetworkReply* reply)
- {
- if (reply->property("cachedData").isValid()) {
- return reply->property("cachedData").toByteArray();
- }
- QByteArray data = reply->readAll();
- cacheReplyData(reply, data);
- return data;
- }
- void RequestClient::cacheReplyData(QNetworkReply* reply, const QByteArray& data)
- {
- reply->setProperty("cachedData", data);
- }
- // 拦截器实现
- void AuthRequestInterceptor::interceptRequest(QNetworkRequest& request)
- {
- const QString token = AppEvent::instance()->jwtToken();
- if (!token.isEmpty()) {
- request.setRawHeader("Authorization", "Bearer " + token.toUtf8());
- }
- request.setRawHeader("Machine-Code", AppEvent::instance()->machineCode().toUtf8());
- request.setRawHeader("Accept-Language", AppEvent::instance()->locale().toUtf8());
- }
- void AuthRequestInterceptor::interceptResponse(QNetworkReply* reply)
- {
- if (reply->error() == QNetworkReply::AuthenticationRequiredError) {
- // 触发token刷新
- emit AppEvent::instance()->refreshTokenNeeded();
- if (AppEvent::instance()->isEnableRefreshToken()) {
- // 刷新token逻辑
- auto client = RequestClient::baseGlobalInstance();
- QJsonObject emptyData;
- QJsonDocument jsonDoc(emptyData);
- HttpResponse response = client->post("/api/auth/refresh-token", jsonDoc);
- if (response.code == 0) {
- QString newToken = response.data.toString();
- AppEvent::instance()->setJwtToken(newToken);
- } else {
- // 刷新失败,需要重新登录
- AppEvent::instance()->setJwtToken("");
- emit AppEvent::instance()->loginExpired(true);
- }
- } else {
- // 直接重新认证
- AppEvent::instance()->setJwtToken("");
- emit AppEvent::instance()->loginExpired(true);
- }
- }
- }
- void ErrorResponseInterceptor::interceptResponse(QNetworkReply* reply)
- {
- if (reply->error() != QNetworkReply::NoError) {
- QByteArray responseData = reply->property("cachedData").toByteArray();
- QString errorMessage;
- if (!responseData.isEmpty()) {
- QJsonDocument jsonResponse = QJsonDocument::fromJson(responseData);
- if (jsonResponse.isObject()) {
- QJsonObject jsonObject = jsonResponse.object();
- if (jsonObject.contains("error")) {
- errorMessage = jsonObject["error"].toString();
- } else if (jsonObject.contains(messageVal)) {
- errorMessage = jsonObject[messageVal].toString();
- }
- }
- }
- if (errorMessage.isEmpty()) {
- errorMessage = reply->errorString();
- }
- emit AppEvent::instance()->errorMessage(errorMessage);
- }
- }
- } // namespace TC
|