在ASP.NET Core 2.1应用程序中,我正在使用HttpClient
发出REST请求。我正在使用DelegatingHandler
来定义一些常见的行为。我在这里注册:
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
s注册为Scoped,但是在两个不同的范围(请求)中,我得到了相同的DelegatingHandler
。我的意思是DelegationgHandler
的构造函数仅被调用一次,并且在更多请求(例如单例)中使用同一实例。其他所有事情都符合预期-其他服务,TypedHttpClients和HttpClient的生命周期还可以。我在构造函数中按断点测试了所有内容,并且在每个实例中都测试了Guid,以便区分实例。
当我将
DelegatingHandler
注册为Transient时,它没有任何区别。TL; DR
DelegatingHandler
像单例一样被解析,即使它们已注册为作用域。这使我沉迷于服务型生活方式。 最佳答案
经过一番调查,我发现即使DelegatingHandler
s通过依赖注入(inject)得以解决,但DelegatingHandler
s的生命周期还是有些出乎意料,并且需要对.NET Core 2.1中的HttpClientFactory
有更深入的了解。HttpClientFactory
每次都会创建新的HttpClient
,但是会在多个HttpMessageHandler
之间共享HttpClient
。您可以在Steve Gordon's article中找到有关此信息的更多信息。
因为DelegatingHandler
的实际实例保存在HttpMessageHandler
内(递归在InnerHandler
属性中),并且HttpMessageHandler
是共享的,所以DelegatingHandler
s的共享方式与共享的HttpMessageHandler
相同,并且具有相同的生命周期。
服务提供者仅在DelegatingHandler
“决定”时用于创建新的HttpClientFactory
,因此每个DelegatingHandler
必须注册为 transient !否则,您将获得不确定的行为。 HttpClientFactory
将尝试重用已经使用的DelegatingHandler
。
解决方法
如果您需要解析DelegatingHandler
中的依赖关系,则可以解析构造函数中的IHttpContextAccessor
,然后通过ServiceProvider
中的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>();
...
}
}