feat/dataset-detail-chart #5
|
|
@ -1,10 +1,9 @@
|
|||
#include "ApiClient.hpp"
|
||||
|
||||
#include <QByteArray>
|
||||
#include "ApiResponseParse.hpp"
|
||||
|
||||
#include <QEventLoop>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonValue>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
|
|
@ -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>}
|
||||
// 便于上层统一通过 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);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -0,0 +1,53 @@
|
|||
#include "ApiResponseParse.hpp"
|
||||
|
||||
#include <QByteArray>
|
||||
#include <QJsonDocument>
|
||||
#include <QJsonParseError>
|
||||
#include <QJsonValue>
|
||||
#include <QNetworkReply>
|
||||
#include <QNetworkRequest>
|
||||
|
||||
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
|
||||
|
|
@ -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
|
||||
|
|
@ -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)
|
||||
|
|
|
|||
Loading…
Reference in New Issue