HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件
HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件
DelegatingHandlers
为了有一个更为清晰的开始,该特性的许多方面已经存在很长时间了,HttpClientFactory 简化了消费这些构建块,使它更加易用,通过更为可组合和清晰的 API。
当发出 Http 请求的时候,通常会需要跨越多个边界,你可能希望通过给定的 HttpClient 应用到所有的请求上.。这包括诸如处理异常,重试失败的请求,记录分析信息或者可能实现缓存层来减少对于重度使用的 Http 调用流。
对于熟悉 ASP.NET Core 的开发者,你可能也熟悉了中间件的概念。DelegatingHandler 提供了几乎相同的概念,但是是反过来的,是用于对外的请求。
你可以定义一个处理器链作为管线,它可以在请求发出之前处理对外的请求,这些处理器可能会修改请求头,检查请求的内容或者记录关于请求的信息。
HttpRequestMessage 依次流过每个处理器直到到达内部最终处理器。该处理器是实际发出 Http 请求。该内部的处理器也是第一个收到响应的处理器。此时,响应的内容依次以相反的顺序穿过处理器管线。又一次,每个处理器可以检查,修改或是根据需要使用响应内容。对于特定的请求你可能对响应的内容应用缓存。
非常类似于 ASP.NET Core 中间件,处理器也可以短路过程并立即返回响应。一种情况是强制特定的规则,例如,你可以创建一个处理器来检查是否一个关键的请求头已经就绪。如果缺失了,就不需要将请求传递给下一个处理器 (避免实际的请求),相反,生成失败的响应以返回给掉用者。
在 HttpClientFactory 和它的扩展之前,你可能需要手工传递处理器的实例到你的 HttpClient 实例的构造函数。该实例将通过这些处理器来处理实际的请求。
通过 IHttpClientFactory ,我们可以更加便捷地应用一个或者多个处理器,在注册命名或者强类型的客户端的时候,可以定义它们。然后,我们通过 HttpClientFactory 获得的 HttpClient 任何实例,将被配置为使用这些管线,我们将通过代码演示这种方便的方式。
DelegatingHandler 抽象类
public abstract class DelegatingHandler : System.Net.Http.HttpMessageHandler
{
protected internal override System.Threading.Tasks.Task<System.Net.Http.HttpResponseMessage> SendAsync (System.Net.Http.HttpRequestMessage request, System.Threading.CancellationToken cancellationToken);
protected override void Dispose (bool disposing);
}
创建处理器
我们将定义两个处理器,为了保持代码简单,它不会实现实际的功能。它们将演示关键的概念,如在后继的文章中所示,达到类似的目的并不需要我们编写自己的处理器。
为了创建处理器,我们可以简单地创建派生自 DelegatingHandler 抽象类的派生类。重写 SendAsync 方法来添加自己的功能。
public class TimingHandler : DelegatingHandler
{
private readonly ILogger<TimingHandler> _logger;
public TimingHandler(ILogger<TimingHandler> logger)
{
_logger = logger;
}
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
var sw = Stopwatch.StartNew();
_logger.LogInformation("Starting request");
var response = await base.SendAsync(request, cancellationToken);
_logger.LogInformation($"Finished request in {sw.ElapsedMilliseconds}ms");
return response;
}
}
对于我们对外的请求,一个计时器将在开始调用之前开始,并等待底层处理器的 SendAsync() 方法返回 HttpResponseMessage。此时对外的请求完成,我们可以记录整个 Http 所花费的时间。
为了更有趣一些,我们创建第二个处理器。它将检查是否存在特定的请求头,如果缺失,它将立即返回响应,短路处理器管线并避免不必要的 Http 调用。
public class ValidateHeaderHandler : DelegatingHandler
{
protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request,
CancellationToken cancellationToken)
{
if (!request.Headers.Contains("X-API-KEY"))
{
return new HttpResponseMessage(HttpStatusCode.BadRequest)
{
Content = new StringContent("You must supply an API key header called X-API-KEY")
};
}
return await base.SendAsync(request, cancellationToken);
}
}
注册处理器
现在,我们已经创建了处理器并希望使用它们,最所的步骤是将它们注册到依赖注入容器中以定义客户端。我们在 Startup 类的 ConfigureServices() 方法中进行处理。
public void ConfigureServices(IServiceCollection services)
{
services.AddTransient<TimingHandler>();
services.AddTransient<ValidateHeaderHandler>();
services.AddHttpClient("github", c =>
{
c.BaseAddress = new Uri("https://api.github.com/");
c.DefaultRequestHeaders.Add("Accept", "application/vnd.github.v3+json");
c.DefaultRequestHeaders.Add("User-Agent", "HttpClientFactory-Sample");
})
.AddHttpMessageHandler<TimingHandler>() // This handler is on the outside and executes first on the way out and last on the way in.
.AddHttpMessageHandler<ValidateHeaderHandler>(); // This handler is on the inside, closest to the request.
}
然后定义客户端,在示例中,我使用命名客户端进行简化,请查看本系列的上一博客来得到关于命名和类型化客户端的内容。 AddHttpClient() 方法此时返回一个 IHttpClientBuilder 对象。可以调用 builder 上增强的扩展方法,这里我们掉用范型的 AddHttpMessageHander() 方法。该方法以范型参数的形式使用处理器的类型。
注册的顺序是有意义的。我们需要先注册最外层的处理器。该处理器将第一个检查请求,并最后看到响应。在本示例中,我们希望计时处理器能够统计整个请求所花费的时间,包括花费在内部处理器上的时间,所以我们首先添加该处理器。然后,我们再次掉用 AddHttpMessageHander() 方法,此时使用我们的 ValidateHeaderHandler 处理器,这是在哪瘦的 HttpClientHander 处理之前的最后一个处理器。
现在我们构建了请求处理中间件管线来定义我们命名的 github 客户端。当请求通过该客户端的时候,它将首先到达 TimingHandler ,然后是 ValidateHeaderHandler。假设请求头存在于请求中,它将被传送并发出请求的 URI 地址上。当响应到达的时候,它首先通过 ValidateHeaderHandler,但它本身没有任何处理,然后,TimingHandler 将计算整个处理时间,最终返回给掉用代码。
总结
本文展示了使用新的扩展创建 DelegatingHandler 是如何容易,并添加到 HttpClient 处理管线中。不过并不希望你编写自己的处理器。Polly 第三方库可以处理常见的场景。
Part 1 – HttpClientFactory in ASP.NET Core 2.1 Part 1 介绍
Part 2 – HttpClientFactory in ASP.NET Core 2.1 Part 2:定义命名和类型化的客户端
Part 3 – HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件
Part 4 – HttpClientFacotry Part 4: 集成 Polly 处理瞬时失效
Part 5 – HttpClientFactory in ASP.NET Core 2.1 Part 5: 日志
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列(二):开始使用ML.NET
· 记一次.NET内存居高不下排查解决与启示
· 探究高空视频全景AR技术的实现原理
· 理解Rust引用及其生命周期标识(上)
· 浏览器原生「磁吸」效果!Anchor Positioning 锚点定位神器解析
· DeepSeek 开源周回顾「GitHub 热点速览」
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· AI与.NET技术实操系列(二):开始使用ML.NET
· 单线程的Redis速度为什么快?
2011-03-05 使用 jQuery dataTables - 2 四种数据来源