3.32JSON 包装器与双重序列化

在与第三方 API 进行 HTTP 远程通信时,通常会返回统一结构的 JSON 响应,例如 ApiResult 类型,其中实际数据存放在 Data 属性中:

启用 JSON 响应反序列化包装器#

在与第三方 API 进行 HTTP 远程通信时,通常会返回统一结构的 JSON 响应,例如 ApiResult<T> 类型,其中实际数据存放在 Data 属性中:

cs
public class ApiResult<T>{    public bool Success { get; set; }    public T? Data { get; set; }    // 实际返回数据}

在未启用 JSON 响应反序列化包装器功能时,每次调用都需要显式指定 ApiResult<T> 类型:

cs
var content = await httpRemoteService.SendAsAsync<ApiResult<string>>(    HttpRequestBuilder.Get("https://furion.net"));

启用方式#

1. 单次启用#

为简化调用流程,可配置 JSON 响应反序列化包装器,使其自动提取 Data 属性内容:

cs
// 配置默认 HTTP 客户端services.AddHttpClient(string.Empty)    .ConfigureOptions(options =>    {        options.JsonResponseWrapper = new JsonResponseWrapper(typeof(ApiResult<>), nameof(ApiResult<>.Data));    });

配置完成后,通过调用 UseJsonResponseWrapper() 启用该功能,之后只需指定目标数据类型,无需重复声明 ApiResult<T>

cs
var content = await httpRemoteService.SendAsAsync<string>(    HttpRequestBuilder.Get("https://furion.net").UseJsonResponseWrapper());

框架将在运行时自动创建 ApiResult<string> 实例,并返回其 Data 属性的值。

2. 全局启用(默认对所有请求生效)#

也可全局启用 JSON 响应反序列化包装器功能,只需设置 UseJsonResponseWrappertrue

cs
// 配置默认 HTTP 客户端services.AddHttpClient(string.Empty)    .ConfigureOptions(options =>    {        options.JsonResponseWrapper = new JsonResponseWrapper(typeof(ApiResult<>), nameof(ApiResult<>.Data));        options.UseJsonResponseWrapper = true;    });

全局启用后,所有请求默认使用包装功能:

cs
var content = await httpRemoteService.SendAsAsync<string>(    HttpRequestBuilder.Get("https://furion.net")); // 无需显式调用 UseJsonResponseWrapper()

3. 单次禁用(覆盖全局设置)#

若需对特定请求禁用该功能,可调用以下方法:

cs
var content = await httpRemoteService.SendAsAsync<ApiResult<string>>(    HttpRequestBuilder.Get("https://furion.net").UseJsonResponseWrapper(false));

默认情况下,未调用 UseJsonResponseWrapper() 表示未启用该功能,此时需传入完整的响应类型,除非全局配置了 UseJsonResponseWrapper = true

自定义结果处理(ResultHandler#

有时除了提取 Data,还需对响应做额外校验或转换。可通过 ResultHandler 回调实现:

cs
// 配置默认 HTTP 客户端services.AddHttpClient(string.Empty)    .ConfigureOptions(options =>    {        options.JsonResponseWrapper = new JsonResponseWrapper(typeof(ApiResult<>), nameof(ApiResult<>.Data))        {            ResultHandler = context =>            {                if (context.Instance is { } instance)                {                     // 可访问包装类型实例,获取其任意属性                    var success = context.GetPropertyValue<bool>(nameof(ApiResult<>.Success));                }                // 例如确保请求成功                context.ResponseMessage.EnsureSuccessStatusCode();                // 返回最终的目标结果(即 Data 的值)                return context.Result;            }        };    });

通过 ResultHandler,您可以在返回最终数据前执行任何自定义逻辑(如校验、转换或异常处理),使请求处理更加灵活。

context 参数的类型为 JsonResponseWrapperContext,包含以下属性和方法:

  • 属性

    • Instance:包装类型的具体实例(如 ApiResult<T>object? 类型)。
    • Result:目标结果(即 Data 的值,object? 类型)。
    • ResponseMessage:响应消息(HttpResponseMessage 类型)。
  • 方法

    • GetPropertyValue<T>(propertyName):获取包装类型的具体类型(即 Instance) 指定属性值。

响应 JSON 双重序列化处理#

在与第三方 API 进行 HTTP 远程通信时,极少数情况下可能会遇到服务器返回的 JSON 数据被意外进行了双重序列化(有时也可能是刻意为之)。例如,本应返回 "{\"id\":1,\"name\":\"furion\"}",却因双重序列化变成了 "\"{\\\"id\\\":10, \\\"name\\\":\\\"furion\\\"}\""。针对这类情况,框架提供了解包支持:

cs
var content = await httpRemoteService.SendAsAsync<YourModel>(    HttpRequestBuilder.Get("https://furion.net").UseJsonResponseStringUnwrap());

通过调用 UseJsonResponseStringUnwrap() 方法启用对 JSON 响应内容的解包处理,这样便能正确地将双重序列化后的 JSON 字符串转换为目标类型(YourModel)。