IHttpClientFactory.CreateClient(customClient) 如何创建 HttpClient 的异步定义?

问题描述 投票:0回答:2

我偶然发现了一个特殊的情况,我想要构建的命名

HttpClient
是使用异步函数运行的,如下所示:

  services.AddHttpClient("customClient", async (serviceProvider, httpClient) => {...});

然后当我尝试从我想要使用它的服务中的

IHttpClientFactory
实例中调用它时,如下所示:

  //Constructor start
  this.httpClient = httpClientFactory.CreateClient("customClient");
  //Constructor end

我最终得到了一个半途而废的

HttpClient
,其中设置了
BaseAddress
但缺少
DefaultRequestHeaders.Authorization
,尽管两者都在相同的
AddHttpClient
异步函数中设置(但
Authorization
标头仅在等待后设置) 1 次通话)。

所以我想知道,就像问题标题中那样:当

CreateClient(name)
被同步调用同时
HttpClient
设置是异步时会发生什么?

难道没有一种机制等待这种定义吗?从我的尝试来看,似乎并非如此。

c# async-await dotnet-httpclient ihttpclientfactory
2个回答
5
投票

所以我想知道,就像问题标题中那样:当同步调用 CreateClient(name) 同时 HttpClient 设置是异步时会发生什么?

设置不允许异步。如果您查看所有重载,它们都不会采用异步委托。

如果你仔细想想,这是有道理的。一般来说,依赖注入会拒绝异步初始化,因为如果它们允许它,那么每个依赖解析都必须被

await
编辑。

难道没有一种机制等待这种定义吗?从我的尝试来看,似乎并非如此。

不。常见的模式有:

  • 如果这是一次性初始化,请在设置 DI 之前执行此操作,并在 DI 设置中使用结果值。
  • 如果必须在启动后完成此操作,请考虑注入异步 HttpClient 工厂(没有内置类型;您必须使用自己的类型)。
  • 如果需要定期执行此操作(例如,刷新访问令牌),那么您真正需要的是一个可以按需(重新)获取令牌的中间件(委托处理程序)。

0
投票

我碰巧发现自己处于你的立场,并得出以下结论:身份验证设置永远不会发生在

services.AddHttpClient()
部分,而是在同一DI的下一行:
.ConfigurePrimaryMessageHandler()

您将配置 NTLM/OAuth/等。在这里,如果您在同一位置设置令牌身份验证,读者会非常清楚会发生什么并且易于维护:

services.AddHttpClient("customClient", ...)
    .ConfigurePrimaryMessageHandler((provider) =>
        new TokenProviderMessageHandler(provider.GetRequiredService<ITokenAccessor>());

serviceProvider 仍然过载,您仍然依赖

IHttpClientFactory
,如果您将流程推迟到实际调用(异步),您甚至可以
await

/// <summary>Middleware for HttpClient calls</summary>
class TokenProviderMessageHandler : DelegatingHandler
{
    string token;
    readonly Task<string> tokenTask;

    TokenProviderMessageHandler(ITokenAccessor accessor)
        // inner handler is necessary, has the same scope (default PooledConnectionIdleTimeout = 2 minutes)
        : base(new HttpClientHandler())
    {
        tokenTask = accessor.GetToken();
    }

    public async Task SendAsync(HttpRequestMessage request, CancellationToken cancellation)
    {
        token ??= await tokenTask;
        // additional renew token logic could be here, depends on your iddletime between calls

        request.Headers.Add(..., $"Bearer {token}");

        return base.SendAsync(request, cancellation);
    }
}
© www.soinside.com 2019 - 2024. All rights reserved.