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: 日志
HttpClientFactory in ASP.NET Core 2.1 Part 3: 对处理器使用对外请求中间件的更多相关文章
- 网络游戏开发-服务器(01)Asp.Net Core中的websocket,并封装一个简单的中间件
先拉开MSDN的文档,大致读一遍 (https://docs.microsoft.com/zh-cn/aspnet/core/fundamentals/websockets) WebSocket 是一 ...
- ASP.NET Core 2.2 基础知识(十二) 发送 HTTP 请求
可以注册 IHttpClientFactory 并将其用于配置和创建应用中的 HttpClient 实例. 这能带来以下好处: 提供一个中心位置,用于命名和配置逻辑 HttpClient 实例. 例如 ...
- asp.net core webapi 统一处理返回值、异常和请求参数验证
现在的开发模式很少用asp.net mvc一个项目直接操作界面和数据库了.大部分都使用前后端分离,更多的是为了让API支持移动端. 后端写webapi的时候必然需要和前端约定请求值和返回值的格式,如果 ...
- Asp.Net Core 3.1 获取不到Post、Put请求的内容 System.NotSupportedException Specified method is not supported
# 问题 是这样的,我.net core 2.1的项目,读取.获取Post请求内容的一段代码,大概这样: [HttpPost] public async Task<IActionResult& ...
- 在ASP.NET Core中用HttpClient(三)——发送HTTP PATCH请求
在前面的两篇文章中,我们讨论了很多关于使用HttpClient进行CRUD操作的基础知识.如果你已经读过它们,你就知道如何使用HttpClient从API中获取数据,并使用HttpClient发送PO ...
- ASP.NET Core 性能优化最佳实践
本文提供了 ASP.NET Core 的性能最佳实践指南. 译文原文地址:https://docs.microsoft.com/en-us/aspnet/core/performance/perfor ...
- ASP.NET Core 中间件之压缩、缓存
前言 今天给大家介绍一下在 ASP.NET Core 日常开发中用的比较多的两个中间件,它们都是出自于微软的 ASP.NET 团队,他们分别是 Microsoft.AspNetCore.Respons ...
- ASP.NET Core 1.1 简介
ASP.NET Core 1.1 于2016年11月16日发布.这个版本包括许多伟大的新功能以及许多错误修复和一般的增强.这个版本包含了多个新的中间件组件.针对Windows的WebListener服 ...
- ASP.NET Core框架揭秘(持续更新中…)
之前写了一系列关于.NET Core/ASP.NET Core的文章,但是大都是针对RC版本.到了正式的RTM,很多地方都发生了改变,所以我会将之前发布的文章针对正式版本的.NET Core 1.0进 ...
- ASP.NET Core的路由[1]:注册URL模式与HttpHandler的映射关系
ASP.NET Core的路由是通过一个类型为RouterMiddleware的中间件来实现的.如果我们将最终处理HTTP请求的组件称为HttpHandler,那么RouterMiddleware中间 ...
随机推荐
- .Net 6 SignalR 实际业务开发中遇到的问题及解决办法
一.SiganlR 使用的协议类型 1.websocket即时通讯协议 2.Server-Sent Events(SSE)服务器事件 3.longpolling 长轮询. 如果客户端开启协商,会按顺序 ...
- .Net 依赖注入深入探索,做一个DI拓展,实现一个简易灵活的 自动依赖注入框架
一.依赖注入相关知识 1.1.依赖注入的原理和优点 依赖注入(DI),是IOC控制反转思想 的实现.由一个DI容器,去统一管理所有的服务生命周期,服务的创建.销毁.获取,都是由DI容器去处理的. 依赖 ...
- AMBA总线架构简介
于是乎,我们想到了总线,用一个统一的接口协议,设计出一个符合要求的总线,然后将ARM核和各种外设模块挂载在总线上,这样,命令和数据似乎便可以在CPU和外设之间自由穿梭. 1 AMBA总线 AMBA,英 ...
- C#的排序方法 OrderBy
using System;using System.Collections.Generic;using System.Linq;using System.Net.Http;using System.R ...
- k8s-NFS系统配置
k8s-NFS系统配置 NFS(network filesystem),nfs文件系统在k8s中主要用于持久化存储,可以被多个pod访问和共享数据. 特点 数据持久性 nfs为k8s的pod提供了一种 ...
- java CAS及各种锁
CAS CAS 缺点:循环会耗时:一次性只能保持一个共享变量的原子性:ABA问题 package juc.cas; import java.util.concurrent.atomic.AtomicI ...
- WebElement的常用属性和方法
WebElement是WebDriver.find_element()方法返回的一个对象,该对象用来描述Web上的一个元素,比如输入框,按钮等.本节介绍WebElement的常用属性和方法. 一.We ...
- MySQL查询BLOB类型的字段
1.MySQL有四种BLOB类型: 1.TinyBlob 最大能容纳255B的数据 2.Blob 最大能容纳65KB的 3.MediumBlob 最大能容纳16MB的数据 4.LongBlob 最大能 ...
- 搭建离线yum源
HTTP方式 安装步骤 系统:CentOS 7.6 yum install -y httpd vi /etc/httpd/conf/httpd.conf <Directory /> Opt ...
- 使用Boost.asio与Boost.beast基于协程连接ws
目录 目录 前言 准备工作 实现 初始化io_context并监听信号 启动连接ws的线程并启动io_context 建立tcp链接(以下步骤皆位于ws函数中) ws握手 传输数据 效果 总结 前言 ...