6.4自定义内容转换器
IObjectContentConverterFactory 默认的内容转换器工厂会返回 ObjectContentConverter 实例,该实例利用 HttpResponseMessage.Content 的 ReadFromJsonAsync 方法,结合 System.Text.Json 序列化库,将 HttpR
自定义对象内容转换器(如序列化)#
IObjectContentConverterFactory 默认的内容转换器工厂会返回 ObjectContentConverter 实例,该实例利用 HttpResponseMessage.Content 的 ReadFromJsonAsync 方法,结合 System.Text.Json 序列化库,将 HttpRequestMessage 对象转换为目标接收类型。然而,这种转换方式在某些特殊类型(例如 DataTable)上可能会遇到反序列化失败的问题。为了应对这种情况,您可以自定义 ObjectContentConverter,以便选择或更换为更适合您需求的 JSON 序列化工具(例如 Newtonsoft.Json)。
自定义时需同时提供泛型和非泛型版本:
// 非泛型版本public class CustomObjectContentConverter : ObjectContentConverter{ /// <inheritdoc /> public override async Task<object?> ReadAsync(Type resultType, HttpContentConverterContext context, CancellationToken cancellationToken = default) { // 获取 HttpResponseMessage 实例 var httpResponseMessage = context.ResponseMessage; // 解析 HttpClient 客户端对应的 JSON 序列化上下文信息 var jsonSerializationContext = HttpRemoteUtility.ResolveJsonSerializationContext(resultType, httpResponseMessage, ServiceProvider); // 获取 JSON 反序列化的值(若需使用 Newtonsoft.Json 进行序列化或反序列化操作,请将以下代码替换为 Newtonsoft.Json 库的相应方法调用) ✅✅✅ var deserializedValue = await httpResponseMessage.Content.ReadFromJsonAsync(jsonSerializationContext.ResultType, jsonSerializationContext.JsonSerializerOptions, cancellationToken); // 获取转换的目标类型值 return jsonSerializationContext.GetResultValue(deserializedValue, httpResponseMessage); }}// 泛型版本public class CustomObjectContentConverter<TResult> : CustomObjectContentConverter, IHttpContentConverter<TResult>{ /// <inheritdoc /> public virtual TResult? Read(HttpContentConverterContext context, CancellationToken cancellationToken = default) => (TResult?)base.Read(typeof(TResult), context, cancellationToken); /// <inheritdoc /> public virtual async Task<TResult?> ReadAsync(HttpContentConverterContext context, CancellationToken cancellationToken = default) => (TResult?)await base.ReadAsync(typeof(TResult), context, cancellationToken);}接着,创建自定义的 IObjectContentConverterFactory 实现:
public sealed class CustomObjectContentConverterFactory : IObjectContentConverterFactory{ /// <inheritdoc /> public IHttpContentConverter<TResult> GetConverter<TResult>(HttpContentConverterContext context) { // 检查 HTTP 响应的内容类型是否为 XML 媒体类型 if (context.ResponseMessage.IsXmlContent()) { return new XmlObjectContentConverter<TResult>(); } return new CustomObjectContentConverter<TResult>(); } /// <inheritdoc /> public IHttpContentConverter GetConverter(Type resultType, HttpContentConverterContext context) { // 检查 HTTP 响应的内容类型是否为 XML 媒体类型 if (context.ResponseMessage.IsXmlContent()) { return new XmlObjectContentConverter(); } return new CustomObjectContentConverter(); }}最后,在 Startup.cs 或 Program.cs 文件中,配置并注册 HttpRemote 服务,以替换默认的对象内容转换器工厂功能:
services.AddHttpRemote(builder =>{ builder.UseObjectContentConverterFactory<CustomObjectContentConverterFactory>();});这样,通过自定义的 ObjectContentConverter 和 IObjectContentConverterFactory 工厂,可以确保在反序列化时使用指定的 JSON 选项,避免潜在的反序列化问题。
自定义内容转换器#
在特定场景下,当框架内置的 IHttpContentConverter 内容转换器无法满足需求时,可以通过自定义 IHttpContentConverter 内容转换器来解决。
例如,为 Span<char> 类型添加内容转换器,可以通过实现 IHttpContentConverter 接口来实现自定义需求。
public class SpanCharContentConverter : HttpContentConverterBase<Span<char>>{ /// <inheritdoc /> public override byte[]? Read(HttpContentConverterContext context, CancellationToken cancellationToken = default) => AsyncUtility.RunSync(() => ReadAsync(context, cancellationToken)); /// <inheritdoc /> public override async Task<Span<char>?> ReadAsync(HttpContentConverterContext context, CancellationToken cancellationToken = default) { var str = await context.ResponseMessage.Content.ReadAsStringAsync(cancellationToken); return str.AsSpan(); }}接下来,可以通过以下两种方式应用自定义内容转换器器:
- 单次请求设置:
HttpRequestBuilder.Post("https://furion.net/") .AddHttpContentConverters(() => [ new SpanCharContentConverter() ]);- 全局配置:
在 Startup.cs 或 Program.cs 文件中,配置并注册 HttpRemote 服务,以启用自定义内容转换器功能:
services.AddHttpRemote(builder =>{ builder.AddHttpContentConverters(() => [ new SpanCharContentConverter() ]);});以下示例展示了如何在发送 HTTP 远程请求时使用 SpanCharContentConverter 内容转换器:
使用 IHttpRemoteService 方式:
// 单次请求添加var span = await httpRemoteService.SendAsAsync<Span<char>>(HttpRequestBuilder.Post("https://furion.net/") .AddHttpContentConverters(() => [ new SpanCharContentConverter() ]));// 全局配置var span = await httpRemoteService.GetAsAsync<Span<char>>("https://furion.net/");使用 IHttpContentConverterFactory 方式:
public class YourService(IHttpContentConverterFactory httpContentConverterFactory) // .NET8+ 支持主构造函数注入{ public async Task<Span<char>?> GetSpanAsync() { var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "https://furion.net/"); using var httpClient = new HttpClient(); var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage); // 调用 ReadAsync 方法将 HttpResponseMessage 对象转换为目标类型实例 var context = new HttpContentConverterContext(httpResponseMessage); return await httpContentConverterFactory.GetConverter<Span<char>>(context).ReadAsync(context); }}自定义泛型内容转换器#
除了具体类型,框架同样支持泛型内容的转换。例如,定义如下泛型转换器:
public class YourGenericClassContentConverter<T> : HttpContentConverterBase<YourGenericClass<T?>>{ /// <inheritdoc /> public override YourGenericClass<T?>? Read(HttpContentConverterContext context, CancellationToken cancellationToken = default) { // 实现同步转换逻辑 } /// <inheritdoc /> public override Task<YourGenericClass<T?>?> ReadAsync(HttpContentConverterContext context, CancellationToken cancellationToken = default) { // 实现异步转换逻辑 }}接下来在 Startup.cs 或 Program.cs 文件中,配置并注册 HttpRemote 服务,以启用自定义泛型内容转换器功能:
services.AddHttpRemote(builder =>{ builder.AddGenericHttpContentConverters(() => [ new(typeof(YourGenericClass<>), typeArgs => (IHttpContentConverter)Activator.CreateInstance(typeof(YourGenericClassContentConverter<>).MakeGenericType(typeArgs[0]))!) ]);});以下示例展示了如何在发送 HTTP 远程请求时使用 YourGenericClassContentConverter 内容转换器:
使用 IHttpRemoteService 方式:
var str = await httpRemoteService.GetAsAsync<YourGenericClass<string>>("https://furion.net/");使用 IHttpContentConverterFactory 方式:
public class YourService(IHttpContentConverterFactory httpContentConverterFactory) // .NET8+ 支持主构造函数注入{ public async Task<YourGenericClass<string>?> GetStringAsync() { var httpRequestMessage = new HttpRequestMessage(HttpMethod.Post, "https://furion.net/"); using var httpClient = new HttpClient(); var httpResponseMessage = await httpClient.SendAsync(httpRequestMessage); // 调用 ReadAsync 方法将 HttpResponseMessage 对象转换为目标类型实例 var context = new HttpContentConverterContext(httpResponseMessage); return await httpContentConverterFactory.GetConverter<YourGenericClass<string>>(context).ReadAsync(context); }}