如何在ASP.NET Core中使用IHttpClientFactory注册带异步配置的命名HttpClient
如何在ASP.NET Core中使用IHttpClientFactory注册带异步配置的命名HttpClient
嗨,这个场景我之前也碰到过——IHttpClientFactory自带的AddHttpClient配置委托是同步的,没法直接用async/await来做异步初始化操作(比如你这里要异步获取Azure AD令牌)。直接写异步委托的话,编译器会直接报错,因为它期望的是同步的Action委托。
下面给你两种实用的解决方案,根据你的具体场景来选:
方案一:用自定义DelegatingHandler处理异步逻辑(推荐)
这种方式最适合像令牌获取这类需要每次请求或定期刷新的异步操作,因为DelegatingHandler的SendAsync方法天然支持异步,还能自动复用HttpClient的生命周期。
首先创建一个专门处理Azure AD令牌的Handler:
public class AzureAdTokenHandler : DelegatingHandler { private readonly IConfiguration _configuration; private readonly DefaultAzureCredential _credential; public AzureAdTokenHandler(IConfiguration configuration) { _configuration = configuration; _credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true }); } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // 读取配置中的Scope var scope = _configuration.GetValue<string>("UserManagementService:Scope") ?? throw new ConfigurationErrorsException("UserManagementService:Scope is missing"); // 异步获取令牌 var token = await _credential.GetTokenAsync( new TokenRequestContext(new[] { scope }), cancellationToken).ConfigureAwait(false); // 把令牌添加到请求头 request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token.Token); // 继续传递请求 return await base.SendAsync(request, cancellationToken).ConfigureAwait(false); } }
然后在Startup/Program.cs里注册HttpClient和这个Handler:
// 注册命名HttpClient,同步配置BaseAddress services.AddHttpClient(HttpClientConstants.UserManagementService, (sp, client) => { var configuration = sp.GetRequiredService<IConfiguration>(); var baseAddress = configuration.GetValue<string>("UserManagementService:BaseUrl") ?? throw new ConfigurationErrorsException("UserManagementService:BaseUrl is missing"); client.BaseAddress = new Uri(baseAddress); }) // 添加自定义的令牌Handler .AddHttpMessageHandler<AzureAdTokenHandler>(); // 注册自定义Handler到DI容器 services.AddTransient<AzureAdTokenHandler>();
方案二:异步初始化一次性配置(适合仅需初始化一次的场景)
如果你的异步操作只需要在HttpClient创建时执行一次(比如获取一个不会变化的配置值),可以用IConfigureOptions来实现异步配置:
首先创建一个配置器类:
public class AsyncUserManagementClientConfigurer : IConfigureOptions<HttpClientFactoryOptions> { private readonly IConfiguration _configuration; private readonly DefaultAzureCredential _credential; public AsyncUserManagementClientConfigurer(IConfiguration configuration) { _configuration = configuration; _credential = new DefaultAzureCredential(new DefaultAzureCredentialOptions { ExcludeSharedTokenCacheCredential = true }); } // 注意:这里用async void是因为IConfigureOptions的Configure方法是同步的,要注意异常处理 public async void Configure(HttpClientFactoryOptions options) { var scope = _configuration.GetValue<string>("UserManagementService:Scope") ?? throw new ConfigurationErrorsException("UserManagementService:Scope is missing"); var token = await _credential.GetTokenAsync( new TokenRequestContext(new[] { scope }), CancellationToken.None).ConfigureAwait(false); // 添加初始化逻辑到HttpClient的配置动作 options.HttpClientActions.Add(client => { client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token.Token); }); } }
然后注册到DI:
// 先注册命名HttpClient,同步配置BaseAddress services.AddHttpClient(HttpClientConstants.UserManagementService, (sp, client) => { var configuration = sp.GetRequiredService<IConfiguration>(); var baseAddress = configuration.GetValue<string>("UserManagementService:BaseUrl") ?? throw new ConfigurationErrorsException("UserManagementService:BaseUrl is missing"); client.BaseAddress = new Uri(baseAddress); }); // 注册异步配置器 services.ConfigureOptions<AsyncUserManagementClientConfigurer>();
⚠️ 注意:这种方式的令牌是一次性获取的,过期后不会自动刷新,所以只适合令牌有效期很长或者不需要刷新的场景,大部分情况下还是推荐方案一。
备注:内容来源于stack exchange,提问作者Shuzheng
相关产品推荐
相关产品推荐

