networkaccessmanager.cpp 18 KB

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