为HttpClientFactory中使用的DelegatingHandler添加动态参数
解决方案:将Client ID传递到HttpRequestMessage以实现Token刷新重试
要解决Handler中无法获取Client ID的问题,核心是将Client ID与每个HttpRequestMessage绑定,推荐使用.NET 5+提供的HttpRequestOptions(类型安全、不会被发送到目标服务器),或兼容旧版本的Properties。以下是具体实现步骤:
1. 传递Client ID到请求选项
修改HttpClientService,不再直接设置HttpClient的全局Authorization头(避免并发冲突),而是将Client ID存入请求选项,并在每个请求中单独设置初始Token:
internal class HttpClientService { private readonly IHttpClientFactory _httpClientFactory; private readonly ITokenService _tokenService; public HttpClientService(IHttpClientFactory httpClientFactory, ITokenService tokenService) { _httpClientFactory = httpClientFactory; _tokenService = tokenService; } public async Task<HttpResponseMessage> SendRequestAsync(string clientId, HttpRequestMessage request, CancellationToken cancellationToken = default) { var client = _httpClientFactory.CreateClient("BasicClient"); // 将Client ID存入请求选项(类型安全,仅在应用内部传递) request.Options.Set("ClientId", clientId); // 设置初始Token var token = await _tokenService.GetTokenAsync(clientId); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token); return await client.SendAsync(request, cancellationToken); } }
2. 在TokenFreshnessHandler中获取Client ID并刷新Token
修正Handler中的判断逻辑(仅在收到401响应时刷新),并从请求选项中提取Client ID:
public class TokenFreshnessHandler : DelegatingHandler { private readonly ITokenService _tokenService; // 通过DI注入Token获取服务 public TokenFreshnessHandler(ITokenService tokenService) { _tokenService = tokenService; } protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var response = await base.SendAsync(request, cancellationToken); // 仅在收到401未授权响应时刷新Token并重试 if (response.StatusCode == HttpStatusCode.Unauthorized) { // 从请求选项中获取Client ID if (request.Options.TryGetValue(new HttpRequestOptionsKey<string>("ClientId"), out var clientId)) { // 获取新Token var newToken = await _tokenService.GetTokenAsync(clientId); // 重置请求的Authorization头 request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", newToken); // 重试请求 response = await base.SendAsync(request, cancellationToken); } } return response; } }
3. 正确注册依赖
确保Handler和相关服务被DI容器正确注册:
// 注册Token服务(根据你的实际实现调整) services.AddScoped<ITokenService, TokenService>(); // 注册自定义Handler services.AddScoped<TokenFreshnessHandler>(); // 配置HttpClient services.AddHttpClient("BasicClient") .AddPolicyHandler(GetRetryPolicy()) .AddHttpMessageHandler<TokenFreshnessHandler>();
关键注意事项
- 避免全局Header并发问题:不要修改
HttpClient.DefaultRequestHeaders,改为在每个HttpRequestMessage中单独设置Authorization头,避免多请求同时修改全局头的冲突。 - 重试策略与Handler的顺序:确保
AddHttpMessageHandler在AddPolicyHandler之后(Handler链从内到外执行),让Token刷新逻辑先于重试策略生效,避免重复重试。 - 兼容旧版本.NET:如果使用
.NET Core 3.1及更早版本,可替换HttpRequestOptions为request.Properties["ClientId"] = clientId,在Handler中通过request.Properties.TryGetValue("ClientId", out var clientIdObj)提取。
内容的提问来源于stack exchange,提问作者user14101378
相关产品推荐
相关产品推荐

