首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >HttpClient DelegatingHandler意外生命周期

HttpClient DelegatingHandler意外生命周期
EN

Stack Overflow用户
提问于 2018-11-09 09:51:58
回答 2查看 9.5K关注 0票数 13

在ASP.NET核心2.1应用程序中,我使用HttpClient发出REST请求。我使用DelegatingHandler来定义一些常见的行为。我在这里登记:

代码语言:javascript
复制
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被解析为单例,尽管它们被注册为作用域。这使我对服务生活方式产生了不满。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 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解析依赖项。

这种方法并不完全是“架构干净”,但它是我找到的唯一解决办法。

示例:

代码语言:javascript
复制
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>();
        ...
    }
}
票数 34
EN

Stack Overflow用户

发布于 2019-03-25 07:05:45

从.Net Core2.2开始,有一种替代方法,无需使用IHttpContextAccessor作为服务定位器来解决范围内的依赖关系。请参阅有关此问题的详细信息,这里

这对于.NET核心控制台应用程序非常有用:

代码语言:javascript
复制
// 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));
}); 
票数 5
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/53223411

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档