You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Blazor Server中DelegatingHandler访问DI服务异常问题咨询

问题原因

你遇到的问题核心是Blazor Server的作用域模型与HttpClient及DelegatingHandler的生命周期不匹配导致的:

  1. IHttpContextAccessor的局限性:Blazor Server中,HttpContext仅在初始HTTP渲染组件时有效,后续按钮点击等SignalR交互属于电路(Circuit)上下文,此时HttpContext可能已失效,或其RequestServices并非当前电路的作用域,导致无法获取Scoped的TokenProvider。
  2. Handler生命周期冲突:通过AddHttpMessageHandler<T>注册的AuthenticationStateHandler默认会被解析为Singleton(与HttpClient的Singleton生命周期匹配),而TokenProvider是Scoped服务。Singleton的Handler无法可靠获取当前电路作用域的Scoped服务,第一次调用可能碰巧复用了初始请求的上下文,第二次调用时上下文已失效,导致GetService<TokenProvider>()返回null。

解决方案

不要依赖IHttpContextAccessor获取作用域服务,改用IServiceScopeFactory在每次请求时创建当前作用域,确保能获取到正确的TokenProvider:

  1. 修改AuthenticationStateHandler,注入IServiceScopeFactory替代IHttpContextAccessor:
public class AuthenticationStateHandler : DelegatingHandler
{
    private readonly IServiceScopeFactory _scopeFactory;

    public AuthenticationStateHandler(IServiceScopeFactory scopeFactory)
    {
        _scopeFactory = scopeFactory;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        using var scope = _scopeFactory.CreateScope();
        var tokenProvider = scope.ServiceProvider.GetRequiredService<TokenProvider>();
        
        var token = await tokenProvider.GetAccessTokenAsync();
        request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", token);

        return await base.SendAsync(request, cancellationToken);
    }
}
  1. 将AuthenticationStateHandler注册为Scoped服务,并调整HttpClient的注册方式:
// 注册Scoped的Handler
services.AddScoped<AuthenticationStateHandler>();

// 为每个HttpClient注入Scoped的Handler实例
services.AddHttpClient<DocumentClient>()
    .AddHttpMessageHandler(sp => sp.GetRequiredService<AuthenticationStateHandler>());
services.AddHttpClient<DocumentAttachmentClient>()
    .AddHttpMessageHandler(sp => sp.GetRequiredService<AuthenticationStateHandler>());
  1. 确保TokenProvider是Scoped注册(这应该是你当前的注册方式):
services.AddScoped<TokenProvider>();

这样修改后,每次HTTP请求都会创建一个新的作用域,获取当前电路上下文的TokenProvider,无论调用哪个HttpClient,都能可靠获取到令牌。

内容的提问来源于stack exchange,提问作者Martin Holý

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.26 04:25:04