沭阳网站建设,网站的排名就是友情链接,开发电子商务网站的主流语言,做cpc不做网站可以吗一、前言前面分享了 .net core HttpClient 使用之掉坑解析#xff08;一#xff09;#xff0c;今天来分享自定义消息处理HttpMessageHandler和PrimaryHttpMessageHandler 的使用场景和区别二、源代码阅读2.1 核心消息管道模型图先贴上一张核心MessageHandler 管道模型的流程… 一、前言前面分享了 .net core HttpClient 使用之掉坑解析一今天来分享自定义消息处理HttpMessageHandler和PrimaryHttpMessageHandler 的使用场景和区别二、源代码阅读2.1 核心消息管道模型图先贴上一张核心MessageHandler 管道模型的流程图图如下HttpClient 中的HttpMessageHandler 负责主要核心的业务HttpMessageHandler 是由MessageHandler 链表结构组成形成一个消息管道模式具体我们一起来看看源代码2.2 Demo代码演示再阅读源代码的时候我们先来看下下面注入HttpClient 的Demo 代码代码如下:services.AddHttpClient(test).ConfigurePrimaryHttpMessageHandler(provider {return new PrimaryHttpMessageHandler(provider);}).AddHttpMessageHandler(provider {return new LogHttpMessageHandler(provider);}).AddHttpMessageHandler(provider {return new Log2HttpMessageHandler(provider);});
上面代码中有两个核心扩展方法分别是ConfigurePrimaryHttpMessageHandler和AddHttpMessageHandler,这两个方法大家可能会有疑问是做什么的呢不错这两个方法就是扩展注册自定义的HttpMessageHandler 如果不注册会有默认的HttpMessageHandler,接下来我们分别来看下提供的扩展方法如下图:图中提供了一系列的AddHttpMessageHandler 扩展方法和ConfigurePrimaryHttpMessageHandler的扩展方法。2.3 AddHttpMessageHandler我们来看看HttpClientBuilderExtensions中的其中一个AddHttpMessageHandler扩展方法代码如下 /// summary/// Adds a delegate that will be used to create an additional message handler for a named see crefHttpClient/./// /summary/// param namebuilderThe see crefIHttpClientBuilder/./param/// param nameconfigureHandlerA delegate that is used to create a see crefDelegatingHandler/./param/// returnsAn see crefIHttpClientBuilder/ that can be used to configure the client./returns/// remarks/// The see paramrefconfigureHandler/ delegate should return a new instance of the message handler each time it/// is invoked./// /remarkspublic static IHttpClientBuilder AddHttpMessageHandler(this IHttpClientBuilder builder, FuncDelegatingHandler configureHandler){if (builder null){throw new ArgumentNullException(nameof(builder));}if (configureHandler null){throw new ArgumentNullException(nameof(configureHandler));}builder.Services.ConfigureHttpClientFactoryOptions(builder.Name, options {options.HttpMessageHandlerBuilderActions.Add(b b.AdditionalHandlers.Add(configureHandler()));});return builder;}
代码中把自定义的DelegatingHandler 方法添加到HttpMessageHandlerBuilderActions中我们再来看看HttpClientFactoryOptions对象源代码如下 /// summary/// An options class for configuring the default see crefIHttpClientFactory/./// /summarypublic class HttpClientFactoryOptions{// Establishing a minimum lifetime helps us avoid some possible destructive cases.//// IMPORTANT: This is used in a resource string. Update the resource if this changes.internal readonly static TimeSpan MinimumHandlerLifetime TimeSpan.FromSeconds(1);private TimeSpan _handlerLifetime TimeSpan.FromMinutes(2);/// summary/// Gets a list of operations used to configure an see crefHttpMessageHandlerBuilder/./// /summarypublic IListActionHttpMessageHandlerBuilder HttpMessageHandlerBuilderActions { get; } new ListActionHttpMessageHandlerBuilder();/// summary/// Gets a list of operations used to configure an see crefHttpClient/./// /summarypublic IListActionHttpClient HttpClientActions { get; } new ListActionHttpClient();/// summary/// Gets or sets the length of time that a see crefHttpMessageHandler/ instance can be reused. Each named/// client can have its own configured handler lifetime value. The default value of this property is two minutes./// Set the lifetime to see crefTimeout.InfiniteTimeSpan/ to disable handler expiry./// /summary/// remarks/// para/// The default implementation of see crefIHttpClientFactory/ will pool the see crefHttpMessageHandler//// instances created by the factory to reduce resource consumption. This setting configures the amount of time/// a handler can be pooled before it is scheduled for removal from the pool and disposal./// /para/// para/// Pooling of handlers is desirable as each handler typically manages its own underlying HTTP connections; creating/// more handlers than necessary can result in connection delays. Some handlers also keep connections open indefinitely/// which can prevent the handler from reacting to DNS changes. The value of see crefHandlerLifetime/ should be/// chosen with an understanding of the applications requirement to respond to changes in the network environment./// /para/// para/// Expiry of a handler will not immediately dispose the handler. An expired handler is placed in a separate pool/// which is processed at intervals to dispose handlers only when they become unreachable. Using long-lived/// see crefHttpClient/ instances will prevent the underlying see crefHttpMessageHandler/ from being/// disposed until all references are garbage-collected./// /para/// /remarkspublic TimeSpan HandlerLifetime{get _handlerLifetime;set{if (value ! Timeout.InfiniteTimeSpan value MinimumHandlerLifetime){throw new ArgumentException(Resources.HandlerLifetime_InvalidValue, nameof(value));}_handlerLifetime value;}}/// summary/// The see crefFunc{T, R}/ which determines whether to redact the HTTP header value before logging./// /summarypublic Funcstring, bool ShouldRedactHeaderValue { get; set; } (header) false;/// summary/// para/// Gets or sets a value that determines whether the see crefIHttpClientFactory/ will/// create a dependency injection scope when building an see crefHttpMessageHandler/./// If cfalse/c (default), a scope will be created, otherwise a scope will not be created./// /para/// para/// This option is provided for compatibility with existing applications. It is recommended/// to use the default setting for new applications./// /para/// /summary/// remarks/// para/// The see crefIHttpClientFactory/ will (by default) create a dependency injection scope/// each time it creates an see crefHttpMessageHandler/. The created scope has the same/// lifetime as the message handler, and will be disposed when the message handler is disposed./// /para/// para/// When operations that are part of see crefHttpMessageHandlerBuilderActions/ are executed/// they will be provided with the scoped see crefIServiceProvider/ via/// see crefHttpMessageHandlerBuilder.Services/. This includes retrieving a message handler/// from dependency injection, such as one registered using/// see crefHttpClientBuilderExtensions.AddHttpMessageHandler{THandler}(IHttpClientBuilder)/./// /para/// /remarkspublic bool SuppressHandlerScope { get; set; }}
源代码中有如下核心List: public IListActionHttpMessageHandlerBuilder HttpMessageHandlerBuilderActions { get; } new ListActionHttpMessageHandlerBuilder();
提供了HttpMessageHandlerBuilder HttpMessageHandler 的构造器列表对象故通过AddHttpMessageHandler可以添加一系列的消息构造器方法对象 我们再来看看这个消息构造器类核心部分代码如下public abstract class HttpMessageHandlerBuilder{/// summary/// Gets or sets the name of the see crefHttpClient/ being created./// /summary/// remarks/// The see crefName/ is set by the see crefIHttpClientFactory/ infrastructure/// and is public for unit testing purposes only. Setting the see crefName/ outside of/// testing scenarios may have unpredictable results./// /remarkspublic abstract string Name { get; set; }/// summary/// Gets or sets the primary see crefHttpMessageHandler/./// /summarypublic abstract HttpMessageHandler PrimaryHandler { get; set; }/// summary/// Gets a list of additional see crefDelegatingHandler/ instances used to configure an/// see crefHttpClient/ pipeline./// /summarypublic abstract IListDelegatingHandler AdditionalHandlers { get; }/// summary/// Gets an see crefIServiceProvider/ which can be used to resolve services/// from the dependency injection container./// /summary/// remarks/// This property is sensitive to the value of/// see crefHttpClientFactoryOptions.SuppressHandlerScope/. If ctrue/c this/// property will be a reference to the applications root service provider. If cfalse/c/// (default) this will be a reference to a scoped service provider that has the same/// lifetime as the handler being created./// /remarkspublic virtual IServiceProvider Services { get; }/// summary/// Creates an see crefHttpMessageHandler/./// /summary/// returns/// An see crefHttpMessageHandler/ built from the see crefPrimaryHandler/ and/// see crefAdditionalHandlers/./// /returnspublic abstract HttpMessageHandler Build();protected internal static HttpMessageHandler CreateHandlerPipeline(HttpMessageHandler primaryHandler, IEnumerableDelegatingHandler additionalHandlers){// This is similar to https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Net.Http.Formatting/HttpClientFactory.cs#L58// but we dont want to take that package as a dependency.if (primaryHandler null){throw new ArgumentNullException(nameof(primaryHandler));}if (additionalHandlers null){throw new ArgumentNullException(nameof(additionalHandlers));}var additionalHandlersList additionalHandlers as IReadOnlyListDelegatingHandler ?? additionalHandlers.ToArray();var next primaryHandler;for (var i additionalHandlersList.Count - 1; i 0; i--){var handler additionalHandlersList[i];if (handler null){var message Resources.FormatHttpMessageHandlerBuilder_AdditionalHandlerIsNull(nameof(additionalHandlers));throw new InvalidOperationException(message);}// Checking for this allows us to catch cases where someone has tried to re-use a handler. That really wont// work the way you want and it can be tricky for callers to figure out.if (handler.InnerHandler ! null){var message Resources.FormatHttpMessageHandlerBuilder_AdditionHandlerIsInvalid(nameof(DelegatingHandler.InnerHandler),nameof(DelegatingHandler),nameof(HttpMessageHandlerBuilder),Environment.NewLine,handler);throw new InvalidOperationException(message);}handler.InnerHandler next;next handler;}return next;}}
HttpMessageHandlerBuilder构造器中有两个核心属性PrimaryHandler 和AdditionalHandlers 细心的同学可以发现AdditionalHandlers是一个IListDelegatingHandler列表也就是说可以HttpClient 可以添加多个DelegatingHandler 即多个HttpMessageHandler 消息处理Handler 但是只能有一个PrimaryHandler Handler同时HttpMessageHandlerBuilder提供了一个抽象的Build方法还有一个CreateHandlerPipeline 方法这个方法主要是把IListDelegatingHandler 和PrimaryHandler 构造成一个MessageHandler 链表结构通过DelegatingHandler 的InnerHandler属性进行连接起来2.4 ConfigurePrimaryHttpMessageHandler public static IHttpClientBuilder ConfigurePrimaryHttpMessageHandler(this IHttpClientBuilder builder, FuncHttpMessageHandler configureHandler){if (builder null){throw new ArgumentNullException(nameof(builder));}if (configureHandler null){throw new ArgumentNullException(nameof(configureHandler));}builder.Services.ConfigureHttpClientFactoryOptions(builder.Name, options {options.HttpMessageHandlerBuilderActions.Add(b b.PrimaryHandler configureHandler());});return builder;}
通过上面的HttpMessageHandlerBuilder 源代码分析ConfigurePrimaryHttpMessageHandler 方法主要是给Builder 中添加PrimaryHandler消息Handler2.5 DefaultHttpMessageHandlerBuilder我们知道在services.AddHttpClient() 方法中会注册默认的DefaultHttpMessageHandlerBuilder 消息构造器方法它继承DefaultHttpMessageHandlerBuilder,那我们来看看它的源代码internal class DefaultHttpMessageHandlerBuilder : HttpMessageHandlerBuilder{public DefaultHttpMessageHandlerBuilder(IServiceProvider services){Services services;}private string _name;public override string Name{get _name;set{if (value null){throw new ArgumentNullException(nameof(value));}_name value;}}public override HttpMessageHandler PrimaryHandler { get; set; } new HttpClientHandler();public override IListDelegatingHandler AdditionalHandlers { get; } new ListDelegatingHandler();public override IServiceProvider Services { get; }public override HttpMessageHandler Build(){if (PrimaryHandler null){var message Resources.FormatHttpMessageHandlerBuilder_PrimaryHandlerIsNull(nameof(PrimaryHandler));throw new InvalidOperationException(message);}return CreateHandlerPipeline(PrimaryHandler, AdditionalHandlers);}
代码中Build 会去调用HttpMessageHandlerBuilder 的CreateHandlerPipeline方法把HttpMessageHandler 构建成一个类似于链表的结构。到这里源代码已经分析完了接下来我们来演示一个Demo来证明上面的核心HttpMessageHandler 流程走向图三、Demo演示证明我们继续来看上面我的Demo代码services.AddHttpClient(test).ConfigurePrimaryHttpMessageHandler(provider {return new PrimaryHttpMessageHandler(provider);}).AddHttpMessageHandler(provider {return new LogHttpMessageHandler(provider);}).AddHttpMessageHandler(provider {return new Log2HttpMessageHandler(provider);});
代码中自定义了两个HttpMessageHandler和一个PrimaryHttpMessageHandler我们再来分别看看Log2HttpMessageHandler、LogHttpMessageHandler 和PrimaryHttpMessageHandler 代码代码很简单就是SendAsync前后输出了Log信息代码如下自定义的PrimaryHttpMessageHandler 代码如下public class PrimaryHttpMessageHandler: DelegatingHandler{private IServiceProvider _provider;public PrimaryHttpMessageHandler(IServiceProvider provider){_provider provider;InnerHandler new HttpClientHandler();}protected async override TaskHttpResponseMessage SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){System.Console.WriteLine(PrimaryHttpMessageHandler Start Log);var response await base.SendAsync(request, cancellationToken);System.Console.WriteLine(PrimaryHttpMessageHandler End Log);return response;}}
Log2HttpMessageHandler 代码如下 public class Log2HttpMessageHandler : DelegatingHandler{private IServiceProvider _provider;public Log2HttpMessageHandler(IServiceProvider provider){_provider provider;//InnerHandler new HttpClientHandler();}protected async override TaskHttpResponseMessage SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){System.Console.WriteLine(LogHttpMessageHandler2 Start Log);var responseawait base.SendAsync(request, cancellationToken);System.Console.WriteLine(LogHttpMessageHandler2 End Log);return response;}}
LogHttpMessageHandler代码如下 public class LogHttpMessageHandler : DelegatingHandler{private IServiceProvider _provider;public LogHttpMessageHandler(IServiceProvider provider){_provider provider;//InnerHandler new HttpClientHandler();}protected async override TaskHttpResponseMessage SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){System.Console.WriteLine(LogHttpMessageHandler Start Log);var responseawait base.SendAsync(request, cancellationToken);System.Console.WriteLine(LogHttpMessageHandler End Log);return response;}}
三个自定义Handler 代码已经完成我们继续添加调用代码如下 /// summary////// /summary/// param nameurl/param/// returns/returnspublic async Taskstring GetBaiduAsync(string url){var client _clientFactory.CreateClient(test);var result await client.GetStringAsync(url);return result;}
现在我们运行访问接口运行后的控制台Log 如下图看到输出结果大家有没有发现跟Asp.net core 中的中间件管道的运行图一样。四、总结HttpClient中HttpMessageHandler可以自定义多个但是只能有一个PrimaryHttpMessageHandler如果添加多个只会被最后面添加的给覆盖添加的一系列Handler 构成一个链式管道模型并且PrimaryHttpMessageHandler 主的消息Handler 是在管道的最外层也就是管道模型中的最后一道Handler。使用场景我们可以通过自定义的MessageHandler 来动态加载请求证书通过数据库的一些信息在自定义的Handler 中加载注入对应的证书这样可以起到动态加载支付证书作用同时可以SendAsync 之前或者之后做一些自己的验证等相关业务大家只需要理解它们的用途自然知道它的强大作用今天就分享到这里。往期精彩回顾【.net core】电商平台升级之微服务架构应用实战.Net Core微服务架构技术栈的那些事Asp.Net Core 中IdentityServer4 授权中心之应用实战Asp.Net Core 中IdentityServer4 授权中心之自定义授权模式Asp.Net Core 中IdentityServer4 授权流程及刷新TokenAsp.Net Core 中IdentityServer4 实战之 Claim详解Asp.Net Core 中IdentityServer4 实战之角色授权详解Asp.Net Core 中间件应用实战中你不知道的那些事Asp.Net Core Filter 深入浅出的那些事-AOPAsp.Net Core EndPoint 终结点路由工作原理解读ASP.NET CORE 内置的IOC解读及使用♥ 给个[在看]是对我最大的支持 ♥