feat/dataset-detail-chart #5
|
|
@ -1,10 +1,9 @@
|
||||||
#include "ApiClient.hpp"
|
#include "ApiClient.hpp"
|
||||||
|
|
||||||
#include <QByteArray>
|
#include "ApiResponseParse.hpp"
|
||||||
|
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
#include <QJsonDocument>
|
#include <QJsonDocument>
|
||||||
#include <QJsonParseError>
|
|
||||||
#include <QJsonValue>
|
|
||||||
#include <QNetworkAccessManager>
|
#include <QNetworkAccessManager>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QNetworkRequest>
|
#include <QNetworkRequest>
|
||||||
|
|
@ -14,36 +13,9 @@ namespace geopro::net {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
constexpr int kHttpStatusUnset = 0;
|
|
||||||
const char* const kContentTypeJson = "application/json";
|
const char* const kContentTypeJson = "application/json";
|
||||||
const char* const kTokenHeader = "geomativeauthorization";
|
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
|
} // namespace
|
||||||
|
|
||||||
struct ApiClient::Impl {
|
struct ApiClient::Impl {
|
||||||
|
|
@ -67,20 +39,7 @@ struct ApiClient::Impl {
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
QObject::connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
loop.exec();
|
loop.exec();
|
||||||
|
return buildResponse(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();
|
|
||||||
}
|
|
||||||
// 即便有传输层错误,服务端仍可能回带 JSON 体(如 4xx);尽量解析。
|
|
||||||
if (!body.isEmpty()) {
|
|
||||||
parseBody(body, resp);
|
|
||||||
}
|
|
||||||
return resp;
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
|
add_library(geopro_net STATIC
|
||||||
crypto/RsaEncryptor.cpp
|
crypto/RsaEncryptor.cpp
|
||||||
ApiClient.cpp
|
ApiClient.cpp
|
||||||
|
ApiResponseParse.cpp
|
||||||
AuthService.cpp)
|
AuthService.cpp)
|
||||||
target_include_directories(geopro_net PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(geopro_net PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_link_libraries(geopro_net PUBLIC OpenSSL::SSL OpenSSL::Crypto Qt6::Core Qt6::Network)
|
target_link_libraries(geopro_net PUBLIC OpenSSL::SSL OpenSSL::Crypto Qt6::Core Qt6::Network)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue