HttpClient DelegatingHandler 意外生命週期
在 ASP.NET Core 2.1 應用程序中,我使用
HttpClient. 我使用DelegatingHandlers 來定義一些常見的行為。我在這裡的註冊:private static void AddRestServiceDataClient<TTypedHttpClient, TTypedHttpClientImpl>(this IServiceCollection services) where TTypedHttpClient : class where TTypedHttpClientImpl : RestServiceDataClient, TTypedHttpClient { var httpClientBuilder = services .AddHttpClient<TTypedHttpClient, TTypedHttpClientImpl>() .AddHttpMessageHandler<CachingHttpDelegatingHandler>() .AddHttpMessageHandler<ExceptionHttpDelegatingHandler>() .AddHttpMessageHandler<LoggingHttpDelegatingHandler>(); } ... // EDIT: You should always register DelegatingHandlers as TRANSIENT (read answer for more). services.AddScoped<ExceptionHttpDelegatingHandler>(); services.AddScoped<CachingHttpDelegatingHandler>(); services.AddScoped<LoggingHttpDelegatingHandler>();我將
DelegatingHandlers 註冊為 Scoped,但在兩個不同的範圍(請求)中我得到相同的DelegatingHandler. 我的意思是建構子DelegationgHandler只被呼叫一次,並且在更多請求(如單例)中使用相同的實例。其他一切都如預期 - 其他服務的生命週期,TypedHttpClients 和 HttpClient 沒問題。我在建構子中通過斷點測試了所有內容,並且在每個實例中都測試了 Guid,以便區分實例。
當我將
DelegatingHandlers 註冊為 Transient 時,它沒有任何區別。TL;DR
DelegatingHandler像單例一樣被解析,即使它們被註冊為作用域。這導致我在服務生活方式上大打折扣。
經過一番調查,我發現儘管
DelegatingHandlers 是通過依賴注入解決的,但 s 的生命週期DelegatingHandler有點出乎意料,需要對HttpClientFactory.NET Core 2.1 有更深入的了解。
HttpClientFactory``HttpClient每次都創建新的,但HttpMessageHandler在多個HttpClients 之間共享。有關這方面的更多資訊,您可以在Steve Gordon 的文章中找到。因為
DelegatingHandlers 的實際實例保存在內部(在屬性HttpMessageHandler中遞歸)並且是共享的,所以s 以相同的方式共享,並且具有與 shared 相同的生命週期。InnerHandler``HttpMessageHandler``DelegatingHandler``HttpMessageHandler服務提供者在這裡僅用於在“決定”
DelegatingHandler時創建新的 s - 因此每個都必須註冊為瞬態!否則你會得到不確定的行為。會嘗試重用已經使用過的 .HttpClientFactory``DelegatingHandler``HttpClientFactory``DelegatingHandler解決方法
如果您需要解決依賴關係,
DelegatingHandler您可以IHttpContextAccessor在建構子中解析,然後通過ServiceProviderin解析依賴關係httpContextAccessor.HttpContext.RequestServices。這種方法並不完全是“架構乾淨”,但它是我發現的唯一解決方法。
例子:
internal class MyDelegatingHandler : DelegatingHandler { private readonly IHttpContextAccessor httpContextAccessor; protected MyDelegatingHandler(IHttpContextAccessor httpContextAccessor) { this.httpContextAccessor = httpContextAccessor; } protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var serviceProvider = this.httpContextAccessor.HttpContext.RequestServices; var myService = serviceProvider.GetRequiredService<IMyService>(); ... } }