networkaccessmanager.cpp 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612
  1. #include "networkaccessmanager.h"
  2. #include "../config/networkconfig.h"
  3. #include "appevent.h"
  4. #include <QBuffer>
  5. #include <QCoreApplication>
  6. #include <QEventLoop>
  7. #include <QFile>
  8. #include <QJsonDocument>
  9. #include <QJsonObject>
  10. #include <QMetaObject>
  11. #include <QUrlQuery>
  12. #include <QtConcurrent>
  13. #include "QPromise"
  14. static const QString messageVal = "msg";
  15. static NetworkAccessManager* namInstance = nullptr;
  16. void cleanupNetworkAccessManager()
  17. {
  18. delete namInstance;
  19. namInstance = nullptr;
  20. }
  21. NetworkAccessManager* NetworkAccessManager::instance()
  22. {
  23. if (!namInstance) {
  24. namInstance = new NetworkAccessManager;
  25. qAddPostRoutine(cleanupNetworkAccessManager);
  26. }
  27. return namInstance;
  28. }
  29. NetworkAccessManager::NetworkAccessManager(QObject* parent)
  30. : QNetworkAccessManager(parent)
  31. {
  32. // #ifdef QT_DEBUG
  33. // connect(this, &QNetworkAccessManager::finished, this, [](QNetworkReply* reply) {
  34. // const auto data = reply->readAll();
  35. // reply->seek(0);
  36. // const auto startTimeValue = reply->property("startTime");
  37. // const QDateTime startTime = startTimeValue.value<QDateTime>();
  38. // const QDateTime endTime = QDateTime::currentDateTime();
  39. // qDebug() << QString("[%1] time:[%2]ms --> %3")
  40. // .arg(endTime.toString("yyyy-MM-dd hh:mm:ss.zzz"))
  41. // .arg((endTime.msecsTo(startTime)))
  42. // .arg(QString::fromUtf8(data));
  43. // });
  44. // #endif
  45. }
  46. QNetworkReply* NetworkAccessManager::createRequest(Operation op,
  47. const QNetworkRequest& request,
  48. QIODevice* outgoingData)
  49. {
  50. QNetworkRequest req(request);
  51. // 记录请求开始时间
  52. QDateTime startTime = QDateTime::currentDateTime();
  53. // 获取操作类型字符串
  54. QString opStr;
  55. switch (op) {
  56. case QNetworkAccessManager::GetOperation:
  57. opStr = "GET";
  58. break;
  59. case QNetworkAccessManager::PostOperation:
  60. opStr = "POST";
  61. break;
  62. case QNetworkAccessManager::PutOperation:
  63. opStr = "PUT";
  64. break;
  65. case QNetworkAccessManager::DeleteOperation:
  66. opStr = "DELETE";
  67. break;
  68. case QNetworkAccessManager::HeadOperation:
  69. opStr = "HEAD";
  70. break;
  71. default:
  72. opStr = "UNKNOWN";
  73. break;
  74. }
  75. qDebug() << QString("[%1] --> %2 %3")
  76. .arg(startTime.toString("yyyy-MM-dd hh:mm:ss.zzz"))
  77. .arg(opStr)
  78. .arg(request.url().toString());
  79. // 默认的请求处理
  80. QString agentStr = QString::fromLatin1("%1/%2 (QNetworkAccessManager %3; %4; %5; %6 bit)")
  81. .arg(QCoreApplication::applicationName(),
  82. QCoreApplication::applicationVersion(),
  83. QLatin1String(qVersion()),
  84. QSysInfo::prettyProductName(),
  85. QLocale::system().name())
  86. .arg(QSysInfo::WordSize);
  87. req.setRawHeader("User-Agent", agentStr.toLatin1());
  88. const QString token = AppEvent::instance()->jwtToken();
  89. if (!token.isEmpty()) {
  90. req.setRawHeader("Authorization", "Bearer " + token.toUtf8());
  91. }
  92. req.setRawHeader("Machine-Code", AppEvent::instance()->machineCode().toUtf8());
  93. req.setRawHeader("Accept-Language", AppEvent::instance()->locale().toUtf8());
  94. const QUrl& url = request.url();
  95. const QString urlPath = url.path();
  96. if (!urlPath.contains("/api/auth/refresh-token") && !urlPath.contains("/api/auth/login")) {
  97. if (AppEvent::instance()->isRefreshToken()) {
  98. emit AppEvent::instance()->refreshTokenNeeded();
  99. }
  100. }
  101. QNetworkReply* reply
  102. = QNetworkAccessManager::createRequest(op,
  103. req,
  104. outgoingData); // 存储开始时间,用于计算请求耗时
  105. reply->setProperty("startTime", startTime);
  106. return reply;
  107. }
  108. namespace TC {
  109. RequestClient::RequestClient(const RequestClientOptions& options)
  110. : m_baseUrl(options.baseURL)
  111. , m_timeout(options.timeout)
  112. , m_networkManager(NetworkAccessManager::instance())
  113. {
  114. if (options.enableDefaultInterceptors) {
  115. setupDefaultInterceptors();
  116. }
  117. }
  118. RequestClient::Ptr RequestClient::create(const QString& baseURL, const RequestClientOptions& options)
  119. {
  120. RequestClientOptions opts = options;
  121. opts.baseURL = baseURL;
  122. return Ptr(new RequestClient(opts));
  123. }
  124. RequestClient::Ptr RequestClient::globalInstance()
  125. {
  126. static RequestClient::Ptr instance = create(NetworkConfigInstance.getApiBaseUrl(), {"", true});
  127. return instance;
  128. }
  129. RequestClient::Ptr RequestClient::baseGlobalInstance()
  130. {
  131. static RequestClient::Ptr instance = create(NetworkConfigInstance.getApiBaseUrl(), {"", false});
  132. return instance;
  133. }
  134. void RequestClient::setupDefaultInterceptors()
  135. {
  136. QMutexLocker locker(&m_interceptorMutex);
  137. // 添加认证拦截器
  138. m_interceptors.push_back(std::make_shared<AuthRequestInterceptor>());
  139. // 添加错误处理拦截器
  140. m_interceptors.push_back(std::make_shared<ErrorResponseInterceptor>());
  141. }
  142. void RequestClient::addInterceptor(std::shared_ptr<Interceptor> interceptor)
  143. {
  144. QMutexLocker locker(&m_interceptorMutex);
  145. if (!m_interceptors.contains(interceptor)) {
  146. m_interceptors.append(interceptor);
  147. }
  148. }
  149. void RequestClient::removeInterceptor(std::shared_ptr<Interceptor> interceptor)
  150. {
  151. QMutexLocker locker(&m_interceptorMutex);
  152. m_interceptors.removeOne(interceptor);
  153. }
  154. void RequestClient::clearInterceptors()
  155. {
  156. QMutexLocker locker(&m_interceptorMutex);
  157. m_interceptors.clear();
  158. }
  159. HttpResponse RequestClient::get(const QString& url, const QVariantMap& params)
  160. {
  161. QScopedPointer<QNetworkReply> reply(sendGetRequest(url, params));
  162. QEventLoop loop;
  163. QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
  164. loop.exec();
  165. return parseReplyData(reply.get());
  166. }
  167. HttpResponse RequestClient::post(const QString& url, const QJsonDocument& data)
  168. {
  169. QScopedPointer<QNetworkReply> reply(sendPostRequest(url, data));
  170. QEventLoop loop;
  171. QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
  172. loop.exec();
  173. return parseReplyData(reply.get());
  174. }
  175. HttpResponse RequestClient::put(const QString& url, const QJsonDocument& data)
  176. {
  177. QScopedPointer<QNetworkReply> reply(sendPutRequest(url, data));
  178. QEventLoop loop;
  179. QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
  180. loop.exec();
  181. return parseReplyData(reply.get());
  182. }
  183. HttpResponse RequestClient::deleteResource(const QString& url)
  184. {
  185. QScopedPointer<QNetworkReply> reply(sendDeleteRequest(url));
  186. QEventLoop loop;
  187. QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
  188. loop.exec();
  189. return parseReplyData(reply.get());
  190. }
  191. HttpResponse RequestClient::upload(const QString& url, QHttpMultiPart* multiPart)
  192. {
  193. QScopedPointer<QNetworkReply> reply(sendUploadRequest(url, multiPart));
  194. QEventLoop loop;
  195. QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
  196. loop.exec();
  197. return parseReplyData(reply.get());
  198. }
  199. QFuture<HttpResponse> RequestClient::getAsync(const QString& url, const QVariantMap& params)
  200. {
  201. auto promise = std::make_shared<QPromise<HttpResponse>>();
  202. auto future = promise->future();
  203. QMetaObject::invokeMethod(
  204. QCoreApplication::instance(),
  205. [this, url, params, promise]() {
  206. try {
  207. auto result = get(url, params);
  208. promise->addResult(result);
  209. promise->finish();
  210. } catch (...) {
  211. promise->finish();
  212. }
  213. },
  214. Qt::QueuedConnection);
  215. return future;
  216. }
  217. QFuture<HttpResponse> RequestClient::postAsync(const QString& url, const QJsonDocument& data)
  218. {
  219. auto promise = std::make_shared<QPromise<HttpResponse>>();
  220. auto future = promise->future();
  221. QMetaObject::invokeMethod(
  222. QCoreApplication::instance(),
  223. [this, url, data, promise]() {
  224. try {
  225. auto result = post(url, data);
  226. promise->addResult(result);
  227. promise->finish();
  228. } catch (...) {
  229. promise->finish();
  230. }
  231. },
  232. Qt::QueuedConnection);
  233. return future;
  234. }
  235. QFuture<HttpResponse> RequestClient::putAsync(const QString& url, const QJsonDocument& data)
  236. {
  237. auto promise = std::make_shared<QPromise<HttpResponse>>();
  238. auto future = promise->future();
  239. QMetaObject::invokeMethod(
  240. QCoreApplication::instance(),
  241. [this, url, data, promise]() {
  242. try {
  243. auto result = put(url, data);
  244. promise->addResult(result);
  245. promise->finish();
  246. } catch (...) {
  247. promise->finish();
  248. }
  249. },
  250. Qt::QueuedConnection);
  251. return future;
  252. }
  253. QFuture<HttpResponse> RequestClient::deleteAsync(const QString& url)
  254. {
  255. auto promise = std::make_shared<QPromise<HttpResponse>>();
  256. auto future = promise->future();
  257. QMetaObject::invokeMethod(
  258. QCoreApplication::instance(),
  259. [this, url, promise]() {
  260. try {
  261. auto result = deleteResource(url);
  262. promise->addResult(result);
  263. promise->finish();
  264. } catch (...) {
  265. promise->finish();
  266. }
  267. },
  268. Qt::QueuedConnection);
  269. return future;
  270. }
  271. QFuture<HttpResponse> RequestClient::uploadAsync(const QString& url, QHttpMultiPart* multiPart)
  272. {
  273. auto promise = std::make_shared<QPromise<HttpResponse>>();
  274. auto future = promise->future();
  275. QMetaObject::invokeMethod(
  276. QCoreApplication::instance(),
  277. [this, url, multiPart, promise]() {
  278. try {
  279. auto result = upload(url, multiPart);
  280. promise->addResult(result);
  281. promise->finish();
  282. } catch (...) {
  283. promise->finish();
  284. }
  285. },
  286. Qt::QueuedConnection);
  287. return future;
  288. }
  289. bool RequestClient::download(const QString& url, const QString& saveFilePath)
  290. {
  291. QScopedPointer<QNetworkReply> reply(sendGetRequest(url, {}));
  292. QFile file(saveFilePath);
  293. if (!file.open(QIODevice::WriteOnly)) {
  294. return false;
  295. }
  296. QEventLoop loop;
  297. QObject::connect(reply.get(), &QNetworkReply::readyRead, [&]() {
  298. file.write(reply->readAll());
  299. });
  300. QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit);
  301. loop.exec();
  302. file.close();
  303. return reply->error() == QNetworkReply::NoError;
  304. }
  305. QFuture<bool> RequestClient::downloadAsync(const QString& url, const QString& saveFilePath)
  306. {
  307. auto promise = std::make_shared<QPromise<bool>>();
  308. auto future = promise->future();
  309. QMetaObject::invokeMethod(
  310. QCoreApplication::instance(),
  311. [this, url, saveFilePath, promise]() {
  312. try {
  313. auto result = download(url, saveFilePath);
  314. promise->addResult(result);
  315. promise->finish();
  316. } catch (...) {
  317. promise->finish();
  318. }
  319. },
  320. Qt::QueuedConnection);
  321. return future;
  322. }
  323. QNetworkReply* RequestClient::sendGetRequest(const QString& url, const QVariantMap& params)
  324. {
  325. m_networkManager->setTransferTimeout(m_timeout);
  326. QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
  327. QUrl fullUrl(base + url);
  328. if (!params.isEmpty()) {
  329. QUrlQuery query;
  330. for (auto it = params.constBegin(); it != params.constEnd(); ++it) {
  331. query.addQueryItem(it.key(), it.value().toString());
  332. }
  333. fullUrl.setQuery(query);
  334. }
  335. QNetworkRequest request(fullUrl);
  336. request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
  337. processRequest(request);
  338. emit requestStarted(fullUrl);
  339. return m_networkManager->get(request);
  340. }
  341. QNetworkReply* RequestClient::sendPostRequest(const QString& url, const QJsonDocument& data)
  342. {
  343. m_networkManager->setTransferTimeout(m_timeout);
  344. QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
  345. QUrl fullUrl(base + url);
  346. QBuffer* buffer = new QBuffer;
  347. buffer->setData(data.toJson());
  348. buffer->open(QIODevice::ReadOnly);
  349. QNetworkRequest request(fullUrl);
  350. request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json");
  351. processRequest(request);
  352. emit requestStarted(fullUrl);
  353. QNetworkReply* reply = m_networkManager->post(request, buffer);
  354. buffer->setParent(reply);
  355. return reply;
  356. }
  357. QNetworkReply* RequestClient::sendPutRequest(const QString& url, const QJsonDocument& data)
  358. {
  359. m_networkManager->setTransferTimeout(m_timeout);
  360. QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
  361. QUrl fullUrl(base + url);
  362. QBuffer* buffer = new QBuffer;
  363. buffer->setData(data.toJson());
  364. buffer->open(QIODevice::ReadOnly);
  365. QNetworkRequest request(fullUrl);
  366. processRequest(request);
  367. emit requestStarted(fullUrl);
  368. QNetworkReply* reply = m_networkManager->put(request, buffer);
  369. buffer->setParent(reply);
  370. return reply;
  371. }
  372. QNetworkReply* RequestClient::sendDeleteRequest(const QString& url)
  373. {
  374. m_networkManager->setTransferTimeout(m_timeout);
  375. QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
  376. QUrl fullUrl(base + url);
  377. QNetworkRequest request(fullUrl);
  378. processRequest(request);
  379. emit requestStarted(fullUrl);
  380. return m_networkManager->deleteResource(request);
  381. }
  382. QNetworkReply* RequestClient::sendUploadRequest(const QString& url, QHttpMultiPart* multiPart)
  383. {
  384. m_networkManager->setTransferTimeout(m_timeout);
  385. QString base = m_baseUrl.isEmpty() ? NetworkConfig::instance().getApiBaseUrl() : m_baseUrl;
  386. QUrl fullUrl(base + url);
  387. QNetworkRequest request(fullUrl);
  388. processRequest(request);
  389. emit requestStarted(fullUrl);
  390. return m_networkManager->post(request, multiPart);
  391. }
  392. HttpResponse RequestClient::parseReplyData(QNetworkReply* reply)
  393. {
  394. HttpResponse response;
  395. if (reply->error() != QNetworkReply::NoError) {
  396. response.code = reply->error();
  397. response.message = reply->errorString();
  398. emit requestFailed(reply->url(), response.message);
  399. return response;
  400. }
  401. const QByteArray responseData = readReplyData(reply);
  402. response.rawData = responseData;
  403. QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData);
  404. if (jsonDoc.isObject()) {
  405. const QJsonObject jsonObj = jsonDoc.object();
  406. if (jsonObj.contains("code")) {
  407. response.code = jsonObj["code"].toInt();
  408. }
  409. if (jsonObj.contains(messageVal)) {
  410. response.message = jsonObj[messageVal].toString();
  411. }
  412. if (jsonObj.contains("data")) {
  413. response.data = jsonObj["data"];
  414. }
  415. }
  416. emit requestFinished(reply->url(), response);
  417. return response;
  418. }
  419. void RequestClient::processRequest(QNetworkRequest& request)
  420. {
  421. QMutexLocker locker(&m_interceptorMutex);
  422. for (auto& interceptor : m_interceptors) {
  423. interceptor->interceptRequest(request);
  424. }
  425. }
  426. void RequestClient::processResponse(QNetworkReply* reply)
  427. {
  428. QMutexLocker locker(&m_interceptorMutex);
  429. for (auto& interceptor : m_interceptors) {
  430. interceptor->interceptResponse(reply);
  431. }
  432. }
  433. QByteArray RequestClient::readReplyData(QNetworkReply* reply)
  434. {
  435. if (reply->property("cachedData").isValid()) {
  436. return reply->property("cachedData").toByteArray();
  437. }
  438. QByteArray data = reply->readAll();
  439. cacheReplyData(reply, data);
  440. return data;
  441. }
  442. void RequestClient::cacheReplyData(QNetworkReply* reply, const QByteArray& data)
  443. {
  444. reply->setProperty("cachedData", data);
  445. }
  446. // 拦截器实现
  447. void AuthRequestInterceptor::interceptRequest(QNetworkRequest& request)
  448. {
  449. const QString token = AppEvent::instance()->jwtToken();
  450. if (!token.isEmpty()) {
  451. request.setRawHeader("Authorization", "Bearer " + token.toUtf8());
  452. }
  453. request.setRawHeader("Machine-Code", AppEvent::instance()->machineCode().toUtf8());
  454. request.setRawHeader("Accept-Language", AppEvent::instance()->locale().toUtf8());
  455. }
  456. void AuthRequestInterceptor::interceptResponse(QNetworkReply* reply)
  457. {
  458. if (reply->error() == QNetworkReply::AuthenticationRequiredError) {
  459. // 触发token刷新
  460. emit AppEvent::instance()->refreshTokenNeeded();
  461. if (AppEvent::instance()->isEnableRefreshToken()) {
  462. // 刷新token逻辑
  463. auto client = RequestClient::baseGlobalInstance();
  464. QJsonObject emptyData;
  465. QJsonDocument jsonDoc(emptyData);
  466. HttpResponse response = client->post("/api/auth/refresh-token", jsonDoc);
  467. if (response.code == 0) {
  468. QString newToken = response.data.toString();
  469. AppEvent::instance()->setJwtToken(newToken);
  470. } else {
  471. // 刷新失败,需要重新登录
  472. AppEvent::instance()->setJwtToken("");
  473. emit AppEvent::instance()->loginExpired(true);
  474. }
  475. } else {
  476. // 直接重新认证
  477. AppEvent::instance()->setJwtToken("");
  478. emit AppEvent::instance()->loginExpired(true);
  479. }
  480. }
  481. }
  482. void ErrorResponseInterceptor::interceptResponse(QNetworkReply* reply)
  483. {
  484. if (reply->error() != QNetworkReply::NoError) {
  485. QByteArray responseData = reply->property("cachedData").toByteArray();
  486. QString errorMessage;
  487. if (!responseData.isEmpty()) {
  488. QJsonDocument jsonResponse = QJsonDocument::fromJson(responseData);
  489. if (jsonResponse.isObject()) {
  490. QJsonObject jsonObject = jsonResponse.object();
  491. if (jsonObject.contains("error")) {
  492. errorMessage = jsonObject["error"].toString();
  493. } else if (jsonObject.contains(messageVal)) {
  494. errorMessage = jsonObject[messageVal].toString();
  495. }
  496. }
  497. }
  498. if (errorMessage.isEmpty()) {
  499. errorMessage = reply->errorString();
  500. }
  501. emit AppEvent::instance()->errorMessage(errorMessage);
  502. }
  503. }
  504. } // namespace TC