networkaccessmanager.cpp 16 KB

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