本文介绍了HttpClient DelegatingHandler 意外生命周期的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

在 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 DelegatingHandlers 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 DelegatingHandlers 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 DelegatingHandlers as Transient it makes no difference.

TL;DR DelegatingHandler 被解析为单例,即使它们被注册为作用域.这让我在服务生活方式上混为一谈.

TL;DR DelegatingHandlers 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 DelegatingHandlers are resolved by dependecy injection, the lifecycle of DelegatingHandlers 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 HttpClients. More information about this you can find in Steve Gordon's article.

因为 DelegatingHandler 的实际实例保存在 HttpMessageHandler 中(在 InnerHandler 属性中递归),而 HttpMessageHandler 是共享,则 DelegatingHandler 以与共享的 HttpMessageHandler 相同的方式共享并具有相同的生命周期.

Because actual instances of DelegatingHandlers are held inside HttpMessageHandler (recursively in InnerHandler property) and HttpMessageHandler is shared, then DelegatingHandlers 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 DelegatingHandlers 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 意外生命周期的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!

09-25 06:23