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

Blazor客户端组件OnInitializedAsync调用HttpClient.GetFromJsonAsync失败

Blazor WebAssembly客户端调用服务端授权API失败问题解决

场景与配置

  • .NET 8 Blazor 采用Auto交互式渲染模式,包含服务端+客户端两个项目
  • 服务端配置PingOne OIDC认证,登录流程正常,能成功跳转验证并返回
  • 服务端Routes.razor已用<CascadingAuthenticationState>包裹路由内容
  • 客户端页面ClientPayeeSearch使用@rendermode InteractiveWebAssembly,并添加[Authorize(Policy = "AnyPayeeAdminRole")]属性
  • 服务端APICacheController/GetClients受[Authorize(Policy = "AnyPayeeAdminRole")]保护

问题现象

客户端页面OnInitializedAsync方法中:

  • AuthenticationStateProvider.GetAuthenticationStateAsync()能获取到已认证用户(IsAuthenticated=true,包含所有Claims)
  • HttpClient.GetFromJsonAsync调用API时,未到达服务端API方法,反而返回PingOne登录重定向的HTML内容,导致JSON反序列化失败
  • 客户端HttpClient为直接new的实例,未做额外配置

解决方案

1. 服务端:配置CORS允许携带凭据(跨域场景必做)

如果客户端与服务端域名/端口不同,需在服务端Program.cs中配置CORS策略,允许请求携带认证Cookie:

builder.Services.AddCors(options =>
{
    options.AddPolicy("AllowBlazorClient", policy =>
    {
        policy.WithOrigins("https://your-client-domain.com") // 替换为实际客户端地址
              .AllowAnyHeader()
              .AllowAnyMethod()
              .AllowCredentials(); // 关键:允许携带Cookie等认证凭据
    });
});

// 中间件顺序:放在UseRouting之后,UseAuthentication之前
app.UseCors("AllowBlazorClient");

2. 客户端:配置HttpClient自动携带认证信息

不要直接new HttpClient,改用Blazor官方推荐的方式,确保请求携带认证凭据:

// 客户端Program.cs
builder.Services.AddHttpClient("ServerAPI", client =>
{
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress);
    client.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json"));
})
.AddHttpMessageHandler<BaseAddressAuthorizationMessageHandler>();

// 注册用于API调用的HttpClient实例
builder.Services.AddScoped(sp => 
    sp.GetRequiredService<IHttpClientFactory>().CreateClient("ServerAPI"));

BaseAddressAuthorizationMessageHandler会自动从AuthenticationStateProvider获取认证令牌,或处理Cookie凭据的传递。

3. 服务端:修改Cookie认证逻辑,API请求返回401而非重定向

服务端默认会将未认证的API请求重定向到登录页,导致客户端拿到HTML而非JSON。需修改Cookie认证事件:

services.AddAuthentication(options =>
{
    options.DefaultScheme = "Cookies";
    options.DefaultChallengeScheme = "OpenIdConnect";
})
.AddCookie(options =>
{
    options.Events.OnRedirectToLogin = context =>
    {
        // 识别AJAX/API请求,返回401而非重定向
        if (context.Request.Headers["X-Requested-With"] == "XMLHttpRequest")
        {
            context.Response.StatusCode = StatusCodes.Status401Unauthorized;
        }
        else
        {
            context.Response.Redirect(context.RedirectUri);
        }
        return Task.CompletedTask;
    };
    // 保留原有OnRedirectToAccessDenied配置
    options.Events.OnRedirectToAccessDenied = async delegate
    {
        await Task.CompletedTask;
    };
})
.AddOpenIdConnect(/* 原有OIDC配置 */);

4. 客户端调用优化

确保认证状态就绪后再发起请求,同时处理异常:

protected override async Task OnInitializedAsync()
{
    var authState = await AuthenticationStateProvider.GetAuthenticationStateAsync();
    if (!authState.User.Identity.IsAuthenticated)
    {
        NavigationManager.NavigateTo("/login");
        return;
    }

    try
    {
        var clients = await HttpClient.GetFromJsonAsync<List<PLP.Library.PayeeAdmin.Models.Client>>("api/cache/GetClients");
        // 处理返回数据
    }
    catch (HttpRequestException ex)
    {
        if (ex.StatusCode == HttpStatusCode.Unauthorized)
        {
            NavigationManager.NavigateTo("/login");
        }
        // 其他异常处理
    }
}

关键原因

Blazor WebAssembly客户端的HttpClient默认不会自动携带认证Cookie,需通过BaseAddressAuthorizationMessageHandler或显式配置请求头实现;同时服务端默认对未认证请求的重定向逻辑,会导致API请求返回HTML而非JSON,需针对AJAX请求做特殊处理。

内容的提问来源于stack exchange,提问作者Tom Regan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.19 17:14:51