日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

.net core HttpClient 使用之消息管道解析(二)

發布時間:2023/12/4 编程问答 28 豆豆
生活随笔 收集整理的這篇文章主要介紹了 .net core HttpClient 使用之消息管道解析(二) 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

一、前言

前面分享了 .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 cref="HttpClient"/>./// </summary>/// <param name="builder">The <see cref="IHttpClientBuilder"/>.</param>/// <param name="configureHandler">A delegate that is used to create a <see cref="DelegatingHandler"/>.</param>/// <returns>An <see cref="IHttpClientBuilder"/> that can be used to configure the client.</returns>/// <remarks>/// The <see paramref="configureHandler"/> delegate should return a new instance of the message handler each time it/// is invoked./// </remarks>public static IHttpClientBuilder AddHttpMessageHandler(this IHttpClientBuilder builder, Func<DelegatingHandler> configureHandler){if (builder == null){throw new ArgumentNullException(nameof(builder));}if (configureHandler == null){throw new ArgumentNullException(nameof(configureHandler));}builder.Services.Configure<HttpClientFactoryOptions>(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 cref="IHttpClientFactory"/>./// </summary>public 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 cref="HttpMessageHandlerBuilder"/>./// </summary>public IList<Action<HttpMessageHandlerBuilder>> HttpMessageHandlerBuilderActions { get; } = new List<Action<HttpMessageHandlerBuilder>>();/// <summary>/// Gets a list of operations used to configure an <see cref="HttpClient"/>./// </summary>public IList<Action<HttpClient>> HttpClientActions { get; } = new List<Action<HttpClient>>();/// <summary>/// Gets or sets the length of time that a <see cref="HttpMessageHandler"/> 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 cref="Timeout.InfiniteTimeSpan"/> to disable handler expiry./// </summary>/// <remarks>/// <para>/// The default implementation of <see cref="IHttpClientFactory"/> will pool the <see cref="HttpMessageHandler"/>/// 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 cref="HandlerLifetime"/> should be/// chosen with an understanding of the application's 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 cref="HttpClient"/> instances will prevent the underlying <see cref="HttpMessageHandler"/> from being/// disposed until all references are garbage-collected./// </para>/// </remarks>public TimeSpan HandlerLifetime{get => _handlerLifetime;set{if (value != Timeout.InfiniteTimeSpan && value < MinimumHandlerLifetime){throw new ArgumentException(Resources.HandlerLifetime_InvalidValue, nameof(value));}_handlerLifetime = value;}}/// <summary>/// The <see cref="Func{T, R}"/> which determines whether to redact the HTTP header value before logging./// </summary>public Func<string, bool> ShouldRedactHeaderValue { get; set; } = (header) => false;/// <summary>/// <para>/// Gets or sets a value that determines whether the <see cref="IHttpClientFactory"/> will/// create a dependency injection scope when building an <see cref="HttpMessageHandler"/>./// If <c>false</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 cref="IHttpClientFactory"/> will (by default) create a dependency injection scope/// each time it creates an <see cref="HttpMessageHandler"/>. 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 cref="HttpMessageHandlerBuilderActions"/> are executed/// they will be provided with the scoped <see cref="IServiceProvider"/> via/// <see cref="HttpMessageHandlerBuilder.Services"/>. This includes retrieving a message handler/// from dependency injection, such as one registered using/// <see cref="HttpClientBuilderExtensions.AddHttpMessageHandler{THandler}(IHttpClientBuilder)"/>./// </para>/// </remarks>public bool SuppressHandlerScope { get; set; }}

源代碼中有如下核心List:

public IList<Action<HttpMessageHandlerBuilder>> HttpMessageHandlerBuilderActions { get; } = new List<Action<HttpMessageHandlerBuilder>>();

提供了HttpMessageHandlerBuilder HttpMessageHandler 的構造器列表對象,故,通過AddHttpMessageHandler可以添加一系列的消息構造器方法對象 我們再來看看這個消息構造器類,核心部分,代碼如下:

public abstract class HttpMessageHandlerBuilder{/// <summary>/// Gets or sets the name of the <see cref="HttpClient"/> being created./// </summary>/// <remarks>/// The <see cref="Name"/> is set by the <see cref="IHttpClientFactory"/> infrastructure/// and is public for unit testing purposes only. Setting the <see cref="Name"/> outside of/// testing scenarios may have unpredictable results./// </remarks>public abstract string Name { get; set; }/// <summary>/// Gets or sets the primary <see cref="HttpMessageHandler"/>./// </summary>public abstract HttpMessageHandler PrimaryHandler { get; set; }/// <summary>/// Gets a list of additional <see cref="DelegatingHandler"/> instances used to configure an/// <see cref="HttpClient"/> pipeline./// </summary>public abstract IList<DelegatingHandler> AdditionalHandlers { get; }/// <summary>/// Gets an <see cref="IServiceProvider"/> which can be used to resolve services/// from the dependency injection container./// </summary>/// <remarks>/// This property is sensitive to the value of/// <see cref="HttpClientFactoryOptions.SuppressHandlerScope"/>. If <c>true</c> this/// property will be a reference to the application's root service provider. If <c>false</c>/// (default) this will be a reference to a scoped service provider that has the same/// lifetime as the handler being created./// </remarks>public virtual IServiceProvider Services { get; }/// <summary>/// Creates an <see cref="HttpMessageHandler"/>./// </summary>/// <returns>/// An <see cref="HttpMessageHandler"/> built from the <see cref="PrimaryHandler"/> and/// <see cref="AdditionalHandlers"/>./// </returns>public abstract HttpMessageHandler Build();protected internal static HttpMessageHandler CreateHandlerPipeline(HttpMessageHandler primaryHandler, IEnumerable<DelegatingHandler> additionalHandlers){// This is similar to https://github.com/aspnet/AspNetWebStack/blob/master/src/System.Net.Http.Formatting/HttpClientFactory.cs#L58// but we don't 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 IReadOnlyList<DelegatingHandler> ?? 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 won't// 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是一個IList<DelegatingHandler>列表,也就是說可以HttpClient 可以添加多個DelegatingHandler 即多個HttpMessageHandler 消息處理Handler 但是只能有一個PrimaryHandler Handler

同時HttpMessageHandlerBuilder提供了一個抽象的Build方法,還有一個CreateHandlerPipeline 方法,這個方法主要是把IList<DelegatingHandler> 和PrimaryHandler 構造成一個MessageHandler 鏈表結構(通過DelegatingHandler 的InnerHandler屬性進行連接起來)

2.4 ConfigurePrimaryHttpMessageHandler

public static IHttpClientBuilder ConfigurePrimaryHttpMessageHandler(this IHttpClientBuilder builder, Func<HttpMessageHandler> configureHandler){if (builder == null){throw new ArgumentNullException(nameof(builder));}if (configureHandler == null){throw new ArgumentNullException(nameof(configureHandler));}builder.Services.Configure<HttpClientFactoryOptions>(builder.Name, options =>{options.HttpMessageHandlerBuilderActions.Add(b => b.PrimaryHandler = configureHandler());});return builder;}

通過上面的HttpMessageHandlerBuilder 源代碼分析ConfigurePrimaryHttpMessageHandler 方法主要是給Builder 中添加PrimaryHandler消息Handler

2.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 IList<DelegatingHandler> AdditionalHandlers { get; } = new List<DelegatingHandler>();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 Task<HttpResponseMessage> 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 Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){System.Console.WriteLine("LogHttpMessageHandler2 Start Log");var response=await 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 Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken){System.Console.WriteLine("LogHttpMessageHandler Start Log");var response=await base.SendAsync(request, cancellationToken);System.Console.WriteLine("LogHttpMessageHandler End Log");return response;}}

三個自定義Handler 代碼已經完成,我們繼續添加調用代碼,如下:

/// <summary>////// </summary>/// <param name="url"></param>/// <returns></returns>public async Task<string> 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 授權流程及刷新Token

  • Asp.Net Core 中IdentityServer4 實戰之 Claim詳解

  • Asp.Net Core 中IdentityServer4 實戰之角色授權詳解

  • Asp.Net Core 中間件應用實戰中你不知道的那些事

  • Asp.Net Core Filter 深入淺出的那些事-AOP

  • Asp.Net Core EndPoint 終結點路由工作原理解讀

  • ASP.NET CORE 內置的IOC解讀及使用


??給個[在看],是對我最大的支持??

總結

以上是生活随笔為你收集整理的.net core HttpClient 使用之消息管道解析(二)的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。