原文:https://www.stevejgordon.co.uk/httpclientfactory-aspnetcore-outgoing-request-middleware-pipeline-delegatinghandlers  
发表于:2018年4月

先前的系列文章中我介绍了一些核心概念,并且展示了ASP.NET Core 2.1中新的IHttpClientFactory的一些示例。前面两个帖子开始已经有一段时间了,我想通过讨论带有handler的“传出请求中间件”的概念来继续本系列。

DelegatingHandlers

首先我们要知道,这部分功能中涉及的许多部件已经存在了很长时间。HttpClientFactory通过更加灵活和清晰的API简化了这些部件的使用。

在发起HTTP请求时,您可能希望通过给定的HttpClient将所有请求实现cross cutting concerns(AOP基于切面的设计)。诸如处理重试失败的信息,记录诊断信息或者实现一个缓存层以减少HTTP的调用次数。

对于熟悉ASP.NET Core的人,您也可能熟悉中间件概念。 DelegatingHandlers提供了几乎相同的概念,但相反,是在发出传出请求时。

您可以将一组处理程序(Handlers)定义为管道,在发送之前,它们(Handlers)会处理传出的HTTP请求。这些处理程序可以以编程方式修改标头,检查请求的主体或者记录有关请求的一些信息。

HttpRequestMessage在它到达最终内部处理程序(final inner handler)之前依次流经每个处理程序。这个处理程序实际上将通过网络分派HTTP请求。这个内部处理程序也将是第一个接收响应的人。此时,响应以相反的顺序通过处理程序的回切线传回。同样,每个处理程序都可以根据需要检查,修改或使用响应。例如,对于某些请求路径,您可能希望应用返回数据的缓存。

图中您可以看到可视化的管道

与ASP.NET Core中间件非常相似,处理程序也可以使流程短路来立即返回响应。这在强制执行某些规则时比较有用。例如,您可以创建一个处理程序,检查传出请求的中是否存在API密钥头(key header)。如果缺少这个,程序将不会把请求传递给下一个处理程序(避免实际的HTTP调用),程序会生成一个失败响应返回给调用者。

在使用IHttpClientFactory之前,您需要将处理程序实例(可以是多个)传递到HttpClient实例的构造函数中。然后,HttpClient将通过这些处理程序处理传出请求。

我们可以为不同的“命名化客户端”或“类型化客户端”使用不同的处理程序配置。

创建处理程序(Creating a handler

我们先定义两个handler,为了保持代码简单,它们的功能不会特别逼真,仅为了展示关键概念。后面的例子中有一些方法可以实现类似的结果,而无需编写我们自己的处理程序。

要创建一个处理程序,我们可以简单地创建一个继承于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;
}
}

我们的例子展示的是传出请求。在发起请求前,StopWatch开始执行,接下来异步执行基类的SendAsync方法并返回一个HttpResponseMessage。

为了让事情变得有趣,让我们创建第二个处理程序。它将检查是否存在特定的header,如果没有,则返回立即响应,通过“短路”来避免不必要的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);
}
}

注册处理程序(Registering handlers

现在我们准备好了处理程序,最后一步是使用依赖注入容器注册它们并定义一个客户端。我们在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.
}

前两行将处理程序分别注册到Service Collection,并且使用 transient,以便在每次创建新的HttpClient时都会提供一个新实例。

接下来定义一个客户端。为方便演示,我们使用命名化客户端(named client)。在这个例子中,AddHttpClient方法返回一个IHttpClientBuilder。我们再调用IHttpClientBuilder的泛型扩展方法AddHttpMessageHandler,此方法将处理程序的类型作为泛型参数。

注册顺序很重要。我们首先注册最外层的处理程序(TimingHandler)。该处理程序将第一个检查请求,并最后一个检查响应。在我们的例子中,我们希望timing handler能够记录整个请求流(request flow)的完整时间,包括任何内部处理程序所用时间,因此我们首先添加它。接着,我们再次调用AddHttpMessageHandler,注册ValidateHeaderHandler。它是内部HttpClientHandler传递请求之前最后的自定义处理程序。

至此,我们在“github”客户端(named client)上定义了一个传出中间件管道。当发起请求时,首先经过TimingHandler,然后是ValidateHeaderHandler。如果请求中包含指定的header,则它将被允发送到请求中的URI。当响应返回时,它将首先经过ValidateHeaderHandler,然后传递给TimingHandler记录用时,最后返回调用的代码。

总结

虽然我已经展示了创建DelegatingHandler并将其添加到HttpClient是多么容易,但在大多数情况下,团队并未意识到可以这样做。一些诸如日志记录的常见需求,已经在IHttpClientFactory中考虑到了(将在后面文章中介绍)。对于更复杂的需求,例如失败后重试,更好的选择是使用名叫Polly的第三方库。微软团队已决定与Polly实现整合。

ASP.NET Core 2.1 中的 HttpClientFactory (Part 3) 使用Handler实现传出请求中间件的更多相关文章

  1. ASP.NET Core 2.1 中的 HttpClientFactory (Part 4) 整合Polly实现瞬时故障处理

    原文:https://www.stevejgordon.co.uk/httpclientfactory-using-polly-for-transient-fault-handling发表于:2018 ...

  2. ASP.NET Core 2.1 中的 HttpClientFactory (Part 2) 定义命名化和类型化的客户端

    原文:https://www.stevejgordon.co.uk/httpclientfactory-named-typed-clients-aspnetcore  发表于:2018年1月 上一篇文 ...

  3. ASP.NET Core 2.1 中的 HttpClientFactory (Part 1) HttpClientFactory介绍

    原文:https://www.stevejgordon.co.uk/introduction-to-httpclientfactory-aspnetcore  发表于:2018年1月 ASP.NET ...

  4. .NET Core 2.1中的HttpClientFactory最佳实践

    ASP.NET Core 2.1中出现一个新的HttpClientFactory功能, 它有助于解决开发人员在使用HttpClient实例从其应用程序发出外部Web请求时可能遇到的一些常见问题. 介绍 ...

  5. 在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务

    在 ASP.NET Core Web API中使用 Polly 构建弹性容错的微服务 https://procodeguide.com/programming/polly-in-aspnet-core ...

  6. ASP.NET Core HTTP 管道中的那些事儿

    前言 马上2016年就要过去了,时间可是真快啊. 上次写完 Identity 系列之后,反响还不错,所以本来打算写一个 ASP.NET Core 中间件系列的,但是中间遇到了很多事情.首先是 NPOI ...

  7. ASP.NET Core 1.0 中的依赖项管理

    var appInsights=window.appInsights||function(config){ function r(config){t[config]=function(){var i= ...

  8. 在ASP.NET Core 1.0中如何发送邮件

    (此文章同时发表在本人微信公众号"dotNET每日精华文章",欢迎右边二维码来关注.) 题记:目前.NET Core 1.0中并没有提供SMTP相关的类库,那么要如何从ASP.NE ...

  9. ASP.NET Core 1.0 中使用 Swagger 生成文档

    github:https://github.com/domaindrivendev/Ahoy 之前文章有介绍在ASP.NET WebAPI 中使用Swagger生成文档,ASP.NET Core 1. ...

随机推荐

  1. Sybase数据库连接配置

    简介 1984年,Mark B. Hiffman和Robert Epstern创建了Sybase公司,并在1987年推出了Sybase数据库产品.SYBASE主要有三种版本,一是UNIX操作系统下运行 ...

  2. 2019_软工实践_Beta(1/5)

    队名:955 组长博客:点这里! 作业博客:点这里! 组员情况 组员1(组长):庄锡荣 过去两天完成了哪些任务 文字/口头描述  检测网站不合理的地方,给组员定下相应时间进度的安排 展示GitHub当 ...

  3. 使用lombok的@Builder的注解的一个坑

    一开发说项目报错 java.lang.Long,java.lang.String,java.lang.String,java.lang.String,java.lang.String,java.lan ...

  4. Java 动态调试技术原理及实践 【基本功】Java动态追踪技术探究

    https://mp.weixin.qq.com/s/ZlNcvwJ_swspifWTLHA92Q https://mp.weixin.qq.com/s/_hSaI5yMvPTWxvFgl-UItA

  5. SpringMVC 给请求路径加上统一前缀

    最开始想到的是通过硬编码的方式手动在每个路径上加上前缀, 后面发现这种方式太不智能了,万一要修改那还不得改死, Spring既然支持EL表达式, 那能不能通过EL表达式的方式去读取配置文件里面的属性来 ...

  6. Android NFC M1卡读写&芯片卡读写(CPU卡读写)(RFID读写)

    版权声明:本文为博主原创文章,遵循 CC 4.0 by-sa 版权协议,转载请附上原文出处链接和本声明.本文链接:https://blog.csdn.net/sgn5200/article/detai ...

  7. thinkphp---404错误页面

    在用thinkphp开发项目的时候,会额外的处理404错误页面,但是我们很多的开发人员,在处理404错误页面的时候,处理方式都是不对的. 普通处理404的操作是通过 $this->error() ...

  8. array_slice

    array_slice  分割数组, 效果相当于 substr 类似字符串操作

  9. SRC漏洞挖掘

    SRC目标搜集 文章类的平台 https://www.anquanke.com/src 百度搜索 首先得知道SRC厂商的关键字,利用脚本搜集一波. 比如[应急响应中心]就可以作为一个关键字.通过搜索引 ...

  10. 【VS开发】Visual C++内存泄露检测—VLD工具使用说明

    Visual C++内存泄露检测-VLD工具使用说明 一.        VLD工具概述 Visual Leak Detector(VLD)是一款用于Visual C++的免费的内存泄露检测工具.他的 ...