冠军

导航

HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件

HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件

原文地址:https://www.stevejgordon.co.uk/httpclientfactory-aspnetcore-outgoing-request-middleware-pipeline-delegatinghandlers

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: 日志

posted on 2022-03-05 19:35  冠军  阅读(64)  评论(0编辑  收藏  举报