Blazor Server自定义DelegatingHandler中无法获取Scoped服务怎么办
问题根本原因
DelegatingHandler的默认生命周期为单例,注册后会被IHttpClientFactory缓存复用,和Blazor Server每个用户连接对应的Circuit Scoped生命周期完全不匹配。你在构造函数注入的ITokenService是handler第一次被创建时所在请求的临时scope实例,后续不会跟着当前用户的Circuit scope更新,自然拿不到最新的token值。
解决方案
方案1:封装Scoped Api代理服务(推荐,适配Blazor生命周期)
直接弃用全局的DelegatingHandler,自己封装一个Scoped的Http请求服务,直接依赖当前Scope的ITokenService,实现最简单且不会出现生命周期不匹配问题:
- 定义代理服务
public interface IApiClient { Task<HttpResponseMessage> GetAsync(string url, CancellationToken cancellationToken = default); // 按需补充Post、Put、Delete等常用请求方法 } public class ApiClient : IApiClient { private readonly HttpClient _httpClient; private readonly ITokenService _tokenService; public ApiClient(HttpClient httpClient, ITokenService tokenService) { _httpClient = httpClient; // 此处注入的ITokenService和App.razor、其他业务组件注入的是当前Circuit Scope下的同一个实例 _tokenService = tokenService; } private async Task AttachAuthorizationHeaderAsync(HttpRequestMessage request) { var token = await _tokenService.TryGetToken(); if (!string.IsNullOrEmpty(token)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("Bearer", token); } } public async Task<HttpResponseMessage> GetAsync(string url, CancellationToken cancellationToken = default) { using var request = new HttpRequestMessage(HttpMethod.Get, url); await AttachAuthorizationHeaderAsync(request); return await _httpClient.SendAsync(request, cancellationToken); } }
- 注册服务
// Program.cs 或 Startup.cs builder.Services.AddScoped<ITokenService, TokenService>(); // 注册带配置的HttpClient builder.Services.AddHttpClient<IApiClient, ApiClient>(client => { client.BaseAddress = new Uri("替换为你的API基础地址"); // 可补充全局超时、默认头等配置 });
- 使用方式
在需要发起请求的Blazor组件中直接注入IApiClient调用对应方法即可,所有请求会自动附加上当前用户的令牌。
方案2:改造DelegatingHandler(仅适用于令牌不存储在Circuit Scope的场景)
如果你的令牌是从独立存储(如Cookie、分布式缓存)获取,而非存在当前Circuit Scope的ITokenService实例中,可以改造DelegatingHandler每次请求时动态获取服务:
public class HttpAuthorizationHandler : DelegatingHandler { private readonly IServiceScopeFactory _serviceScopeFactory; public HttpAuthorizationHandler(IServiceScopeFactory serviceScopeFactory) { _serviceScopeFactory = serviceScopeFactory; } protected async override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { using var scope = _serviceScopeFactory.CreateScope(); var tokenService = scope.ServiceProvider.GetRequiredService<ITokenService>(); var token = await tokenService.TryGetToken(); if (!string.IsNullOrEmpty(token)) { request.Headers.Authorization = new System.Net.Http.Headers.AuthenticationHeaderValue("bearer", token); } return await base.SendAsync(request, cancellationToken); } }
注册配置:
builder.Services.AddTransient<HttpAuthorizationHandler>(); builder.Services.AddHttpClient("DefaultApiClient", client => { client.BaseAddress = new Uri("替换为你的API基础地址"); }).AddHttpMessageHandler<HttpAuthorizationHandler>();
内容的提问来源于stack exchange,提问作者Dumas.DED
相关产品推荐
相关产品推荐

