5.14JSON 包装器与双重序列化
在与第三方 API 进行 HTTP 远程通信时,通常会返回统一结构的 JSON 响应,例如 ApiResult 类型,其中实际数据存放在 Data 属性中:
启用 JSON 响应反序列化包装器#
在与第三方 API 进行 HTTP 远程通信时,通常会返回统一结构的 JSON 响应,例如 ApiResult<T> 类型,其中实际数据存放在 Data 属性中:
public class ApiResult<T>{ public bool Success { get; set; } public T? Data { get; set; } // 实际返回数据}HTTP 声明式请求通过 JsonResponseWrapperAttribute 特性来启用 JSON 响应反序列化包装器。相应的 HTTP 声明式提取器实现为 JsonResponseWrapperDeclarativeExtractor 类型,该类型负责解析 JsonResponseWrapperAttribute 特性并构建 HttpRequestBuilder 实例所需的启用 JSON 响应反序列化包装器配置。
在未启用 JSON 响应反序列化包装器功能时,每次调用都需要显式指定 ApiResult<T> 类型:
public interface IHttpService : IHttpDeclarative{ [Get("https://furion.net")] Task<ApiResult<string>> GetStringAsync(); [Get("https://furion.net/")] Task<ApiResult<JsonModel>> GetJsonModelAsync();}启用方式#
1. 单次启用#
为简化调用流程,可配置 JSON 响应反序列化包装器,使其自动提取 Data 属性内容:
// 配置默认 HTTP 客户端services.AddHttpClient(string.Empty) .ConfigureOptions(options => { options.JsonResponseWrapper = new JsonResponseWrapper(typeof(ApiResult<>), nameof(ApiResult<>.Data)); });配置完成后,通过 [JsonResponseWrapper] 启用该功能,之后只需指定目标数据类型,无需重复声明 ApiResult<T>:
// 在接口定义上应用,影响所有方法[JsonResponseWrapper]public interface IHttpService : IHttpDeclarative{ // 默认自动应用 [Get("https://furion.net/")] Task<string> GetStringAsync(); // 在方法上应用 [JsonResponseWrapper] // 可显示启用(无需) [Get("https://furion.net/")] Task<string> GetStringAsync(); [JsonResponseWrapper(false)] // 禁用 JSON 响应反序列化包装器,需传入完整的响应类型 [Get("https://furion.net/")] Task<ApiResult<JsonModel>> GetJsonModelAsync();}框架将在运行时自动创建 ApiResult<string> 实例,并返回其 Data 属性的值。
2. 全局启用(默认对所有请求生效)#
也可全局启用 JSON 响应反序列化包装器功能,只需设置 UseJsonResponseWrapper 为 true:
// 配置默认 HTTP 客户端services.AddHttpClient(string.Empty) .ConfigureOptions(options => { options.JsonResponseWrapper = new JsonResponseWrapper(typeof(ApiResult<>), nameof(ApiResult<>.Data)); options.UseJsonResponseWrapper = true; });全局启用后,所有请求默认使用包装功能:
// [JsonResponseWrapper] // 无需显式设置 [JsonResponseWrapper]public interface IHttpService : IHttpDeclarative{ [Get("https://furion.net/")] Task<string> GetStringAsync();}3. 单次禁用(覆盖全局设置)#
若需对特定请求禁用该功能,可设置 [JsonResponseWrapper(false)] 特性。
自定义结果处理(ResultHandler)#
有时除了提取 Data,还需对响应做额外校验或转换。可通过 ResultHandler 回调实现:
// 配置默认 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) 指定属性值。
JsonResponseWrapperAttribute 包含以下构造函数和属性:
-
构造函数:
new():作用于方法或接口,启用JSON响应反序列化包装器。new(enabled):作用于方法或接口,设置是否启用JSON响应反序列化包装器。
-
属性:
Enabled:是否启用(bool类型),默认值为true(启用)。
响应 JSON 双重序列化处理#
在与第三方 API 进行 HTTP 远程通信时,极少数情况下可能会遇到服务器返回的 JSON 数据被意外进行了双重序列化(有时也可能是刻意为之)。例如,本应返回 "{\"id\":1,\"name\":\"furion\"}",却因双重序列化变成了 "\"{\\\"id\\\":10, \\\"name\\\":\\\"furion\\\"}\""。针对这类情况,框架提供了解包支持。
HTTP 声明式请求通过 JsonResponseStringUnwrapAttribute 特性来启用 JSON 响应内容字符串的解包处理。相应的 HTTP 声明式提取器实现为 JsonResponseStringUnwrapDeclarativeExtractor 类型,该类型负责解析 JsonResponseStringUnwrapAttribute 特性并构建 HttpRequestBuilder 实例所需的启用 JSON 响应内容字符串的解包处理配置。
// 在接口定义上应用,影响所有方法[JsonResponseStringUnwrap]public interface IHttpService : IHttpDeclarative{ // 默认自动应用 [Get("https://furion.net/")] Task<YourModel> GetAsync(); // 在方法上应用 [JsonResponseStringUnwrap] // 可显示启用(无需) [Get("https://furion.net/")] Task<YourModel> GetAsync(); [JsonResponseStringUnwrap(false)] // 禁用 JSON 响应内容字符串的解包处理 [Get("https://furion.net/")] Task<YourModel>> GetAsync();}若需对特定请求禁用该功能,可设置 [JsonResponseStringUnwrap(false)] 特性。
JsonResponseStringUnwrapAttribute 包含以下构造函数和属性:
-
构造函数:
new():作用于方法或接口,启用JSON响应内容字符串的解包处理。new(enabled):作用于方法或接口,设置是否启用JSON响应内容字符串的解包处理。
-
属性:
Enabled:是否启用(bool类型),默认值为true(启用)。