From c90ea83a0423f4d31275af27abaeb573e251d03c Mon Sep 17 00:00:00 2001 From: gaozheng Date: Thu, 11 Jun 2026 19:43:37 +0800 Subject: [PATCH] =?UTF-8?q?refactor(net):=20=E6=8A=BD=E5=87=BA=20buildResp?= =?UTF-8?q?onse=EF=BC=8Csync/async=20=E5=85=B1=E7=94=A8=E5=93=8D=E5=BA=94?= =?UTF-8?q?=E8=A7=A3=E6=9E=90=EF=BC=88DRY=EF=BC=8C=E8=A1=8C=E4=B8=BA?= =?UTF-8?q?=E4=B8=8D=E5=8F=98=EF=BC=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/net/ApiClient.cpp | 47 ++------------------------------ src/net/ApiResponseParse.cpp | 53 ++++++++++++++++++++++++++++++++++++ src/net/ApiResponseParse.hpp | 12 ++++++++ src/net/CMakeLists.txt | 1 + 4 files changed, 69 insertions(+), 44 deletions(-) create mode 100644 src/net/ApiResponseParse.cpp create mode 100644 src/net/ApiResponseParse.hpp diff --git a/src/net/ApiClient.cpp b/src/net/ApiClient.cpp index 30fc5b4..c63e3ff 100644 --- a/src/net/ApiClient.cpp +++ b/src/net/ApiClient.cpp @@ -1,10 +1,9 @@ #include "ApiClient.hpp" -#include +#include "ApiResponseParse.hpp" + #include #include -#include -#include #include #include #include @@ -14,36 +13,9 @@ namespace geopro::net { namespace { -constexpr int kHttpStatusUnset = 0; const char* const kContentTypeJson = "application/json"; const char* const kTokenHeader = "geomativeauthorization"; -// 把响应体 JSON 解析进信封。解析失败或非对象时把原文/错误写入 rawError。 -void parseBody(const QByteArray& body, ApiResponse& resp) { - if (body.isEmpty()) { - resp.rawError = QStringLiteral("empty response body"); - return; - } - QJsonParseError perr{}; - const QJsonDocument doc = QJsonDocument::fromJson(body, &perr); - if (perr.error != QJsonParseError::NoError || !doc.isObject()) { - resp.rawError = QStringLiteral("JSON parse error: %1; body: %2") - .arg(perr.errorString(), QString::fromUtf8(body)); - return; - } - const QJsonObject obj = doc.object(); - resp.code = obj.value(QStringLiteral("code")).toInt(); - resp.msg = obj.value(QStringLiteral("msg")).toString(); - const QJsonValue dataVal = obj.value(QStringLiteral("data")); - if (dataVal.isObject()) { - resp.data = dataVal.toObject(); - } else { - // data 可能是标量(如 verifyCodeCheck 返回 true)。包成 {"value": } - // 便于上层统一通过 data["value"] 取用,同时不丢信息。 - resp.data = QJsonObject{{QStringLiteral("value"), dataVal}}; - } -} - } // namespace struct ApiClient::Impl { @@ -67,20 +39,7 @@ struct ApiClient::Impl { QEventLoop loop; QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); - - ApiResponse resp; - const QVariant statusVar = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); - resp.httpStatus = statusVar.isValid() ? statusVar.toInt() : kHttpStatusUnset; - - const QByteArray body = reply->readAll(); - if (reply->error() != QNetworkReply::NoError) { - resp.rawError = reply->errorString(); - } - // 即便有传输层错误,服务端仍可能回带 JSON 体(如 4xx);尽量解析。 - if (!body.isEmpty()) { - parseBody(body, resp); - } - return resp; + return buildResponse(reply); } }; diff --git a/src/net/ApiResponseParse.cpp b/src/net/ApiResponseParse.cpp new file mode 100644 index 0000000..69aee57 --- /dev/null +++ b/src/net/ApiResponseParse.cpp @@ -0,0 +1,53 @@ +#include "ApiResponseParse.hpp" + +#include +#include +#include +#include +#include +#include + +namespace geopro::net { + +namespace { +constexpr int kHttpStatusUnset = 0; + +void parseBody(const QByteArray& body, ApiResponse& resp) { + if (body.isEmpty()) { + resp.rawError = QStringLiteral("empty response body"); + return; + } + QJsonParseError perr{}; + const QJsonDocument doc = QJsonDocument::fromJson(body, &perr); + if (perr.error != QJsonParseError::NoError || !doc.isObject()) { + resp.rawError = QStringLiteral("JSON parse error: %1; body: %2") + .arg(perr.errorString(), QString::fromUtf8(body)); + return; + } + const QJsonObject obj = doc.object(); + resp.code = obj.value(QStringLiteral("code")).toInt(); + resp.msg = obj.value(QStringLiteral("msg")).toString(); + const QJsonValue dataVal = obj.value(QStringLiteral("data")); + if (dataVal.isObject()) { + resp.data = dataVal.toObject(); + } else { + resp.data = QJsonObject{{QStringLiteral("value"), dataVal}}; + } +} +} // namespace + +ApiResponse buildResponse(QNetworkReply* reply) { + ApiResponse resp; + const QVariant statusVar = reply->attribute(QNetworkRequest::HttpStatusCodeAttribute); + resp.httpStatus = statusVar.isValid() ? statusVar.toInt() : kHttpStatusUnset; + const QByteArray body = reply->readAll(); + if (reply->error() != QNetworkReply::NoError) { + resp.rawError = reply->errorString(); + } + if (!body.isEmpty()) { + parseBody(body, resp); + } + return resp; +} + +} // namespace geopro::net diff --git a/src/net/ApiResponseParse.hpp b/src/net/ApiResponseParse.hpp new file mode 100644 index 0000000..19204a8 --- /dev/null +++ b/src/net/ApiResponseParse.hpp @@ -0,0 +1,12 @@ +#pragma once +#include "ApiClient.hpp" // for geopro::net::ApiResponse + +class QNetworkReply; + +namespace geopro::net { + +// 从一个【已完成】的 reply 读取状态码/响应体/错误并解析为 ApiResponse。 +// 不等待、不删除 reply(调用方负责生命周期)。供同步 await 与异步 ApiCall 共用。 +ApiResponse buildResponse(QNetworkReply* reply); + +} // namespace geopro::net diff --git a/src/net/CMakeLists.txt b/src/net/CMakeLists.txt index 4741214..99d533b 100644 --- a/src/net/CMakeLists.txt +++ b/src/net/CMakeLists.txt @@ -3,6 +3,7 @@ find_package(Qt6 COMPONENTS Core Network REQUIRED) add_library(geopro_net STATIC crypto/RsaEncryptor.cpp ApiClient.cpp + ApiResponseParse.cpp AuthService.cpp) target_include_directories(geopro_net PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) target_link_libraries(geopro_net PUBLIC OpenSSL::SSL OpenSSL::Crypto Qt6::Core Qt6::Network)