在ASP.NET核心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注册为作用域,但在两个不同的范围(请求)中,我得到了相同的DelegatingHandler。我的意思是,DelegationgHandler的构造函数只被调用一次,并且在更多的请求(比如单例)中使用相同的实例。其他一切都如预期的一样--其他服务的生命周期、TypedHttpClients和HttpClient都很好。
我在构造函数中通过断点测试了所有的东西,并且在每个实例中都测试了Guid,这样我就可以区分实例了。
当我将DelegatingHandler注册为瞬态的时候,没有什么区别。
TL;DR DelegatingHandlers被解析为单例,尽管它们被注册为作用域。这使我对服务生活方式产生了不满。
发布于 2018-11-09 13:51:43
经过一些调查后,我发现尽管DelegatingHandler是通过依赖注入解决的,但DelegatingHandler的生命周期有点出乎意料,需要更深入地了解.NET Core2.1中的HttpClientFactory。
HttpClientFactory每次都会创建新的HttpClient,但是可以在多个HttpClient上共享HttpMessageHandler。更多信息可以在史蒂夫·戈登的文章中找到。
因为DelegatingHandler的实际实例保存在HttpMessageHandler中(在InnerHandler属性中是递归的),而HttpMessageHandler是共享的,那么DelegatingHandler的共享方式与共享HttpMessageHandler具有相同的生命周期。
当DelegatingHandler“决定”时,服务提供者只用于创建新的DelegatingHandler --因此必须将每个DelegatingHandler注册为临时的!否则你就会有不确定的行为。HttpClientFactory将尝试重用已经使用过的DelegatingHandler。
解决方案
如果需要解析DelegatingHandler中的依赖项,可以在构造函数中解析IHttpContextAccessor,然后在httpContextAccessor.HttpContext.RequestServices中通过ServiceProvider解析依赖项。
这种方法并不完全是“架构干净”,但它是我找到的唯一解决办法。
示例:
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>();
...
}
}发布于 2019-03-25 07:05:45
从.Net Core2.2开始,有一种替代方法,无需使用IHttpContextAccessor作为服务定位器来解决范围内的依赖关系。请参阅有关此问题的详细信息,这里。
这对于.NET核心控制台应用程序非常有用:
// configure a client pipeline with the name "MyTypedClient"
...
// then
services.AddTransient<MyTypedClient>((s) =>
{
var factory = s.GetRequiredService<IHttpMessageHandlerFactory>();
var handler = factory.CreateHandler(nameof(MyTypedClient));
var otherHandler = s.GetRequiredService<MyOtherHandler>();
otherHandler.InnerHandler = handler;
return new MyTypedClient(new HttpClient(otherHandler, disposeHandler: false));
}); https://stackoverflow.com/questions/53223411
复制相似问题