问题描述
在 ASP.NET Core 2.1 应用程序中,我使用 HttpClient
发出 REST 请求.我使用 DelegatingHandler
来定义一些常见的行为.我在这里注册:
In ASP.NET Core 2.1 app I am making REST request using HttpClient
. I am using DelegatingHandler
s to define some common behavior. The registration i here:
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>();
我将 DelegatingHandler
注册为 Scoped,但在两个不同的范围(请求)中我得到相同的 DelegatingHandler
.我的意思是 DelegationgHandler
的构造函数只被调用一次,并且同一个实例在更多请求中使用(如单例).其他一切都符合预期 - 其他服务的生命周期,TypedHttpClients 和 HttpClient 没问题.
I register DelegatingHandler
s as Scoped, but in two different scopes (requests) I get the same DelegatingHandler
. I mean that the constructor of DelegationgHandler
is being called only once and the same instance is used across more requests (like singleton). Everything else is as expected - life cycle of other services, TypedHttpClients and HttpClient is ok.
我在构造函数中通过断点测试了所有内容,并且在每个实例中都测试了 Guid,以便我可以区分实例.
I tested everything by breakpoint in constructor and I have testing Guid in every instance so I can distinguish instances.
当我将 DelegatingHandler
注册为 Transient 时,它没有任何区别.
When I register DelegatingHandler
s as Transient it makes no difference.
TL;DR DelegatingHandler
被解析为单例,即使它们被注册为作用域.这让我在服务生活方式上混为一谈.
TL;DR DelegatingHandler
s are resolved like singleton even though they are registered as scoped. And this causes me mishmash in service lifestyles.
推荐答案
经过一番调查,我发现即使 DelegatingHandler
是通过依赖注入解决的,DelegatingHandler
的生命周期code>s 有点意外,需要更深入地了解 .NET Core 2.1 中的 HttpClientFactory
.
After some investigation I found out that even though DelegatingHandler
s are resolved by dependecy injection, the lifecycle of DelegatingHandler
s is a bit unexpected and requires deeper knowledge of HttpClientFactory
in .NET Core 2.1.
HttpClientFactory
每次都会创建新的 HttpClient
,但在多个 HttpClient
之间共享 HttpMessageHandler
.您可以在 Steve Gordon 的文章中找到有关这方面的更多信息.
HttpClientFactory
creates new HttpClient
every time, but shares HttpMessageHandler
across multiple HttpClient
s. More information about this you can find in Steve Gordon's article.
因为 DelegatingHandler
的实际实例保存在 HttpMessageHandler
中(在 InnerHandler
属性中递归),而 HttpMessageHandler
是共享,那么 DelegatingHandler
的共享方式和共享的 HttpMessageHandler
具有相同的生命周期.
Because actual instances of DelegatingHandler
s are held inside HttpMessageHandler
(recursively in InnerHandler
property) and HttpMessageHandler
is shared, then DelegatingHandler
s are shared the same way and have same lifecycle as the shared HttpMessageHandler
.
这里的服务提供者仅用于在 HttpClientFactory
决定"时创建新的 DelegatingHandler
- 因此每个 DelegatingHandler
必须注册为瞬态!否则你会得到不确定的行为.HttpClientFactory
会尝试重用已经使用过的 DelegatingHandler
.
Service provider is here used only for creating new DelegatingHandler
s when HttpClientFactory
"decides to" - thus every DelegatingHandler
must be registered as transient! Otherwise you would get non-deterministic behavior. HttpClientFactory
would try to reuse already used DelegatingHandler
.
解决方法
如果需要在DelegatingHandler
中解析依赖,可以在构造函数中解析IHttpContextAccessor
,然后在httpContextAccessor中通过
.ServiceProvider
解析依赖.HttpContext.RequestServices
If you need to resolve dependencies in DelegatingHandler
you can resolve IHttpContextAccessor
in constructor and then resolve dependencies by ServiceProvider
in httpContextAccessor.HttpContext.RequestServices
.
这种方法并不完全是架构上干净的",但它是我找到的唯一解决方法.
This approach is not exactly "architecturally clean" but it is the only workaround I have found.
示例:
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>();
...
}
}
这篇关于HttpClient DelegatingHandler 意外生命周期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!