Blazor客户端组件OnInitializedAsync调用HttpClient.GetFromJsonAsync失败
Blazor WebAssembly客户端调用服务端授权API失败问题解决
场景与配置
- .NET 8 Blazor 采用Auto交互式渲染模式,包含服务端+客户端两个项目
- 服务端配置PingOne OIDC认证,登录流程正常,能成功跳转验证并返回
- 服务端
Routes.razor已用<CascadingAuthenticationState>包裹路由内容 - 客户端页面
ClientPayeeSearch使用@rendermode InteractiveWebAssembly,并添加[Authorize(Policy = "AnyPayeeAdminRole")]属性 - 服务端API
CacheController/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
相关产品推荐
相关产品推荐

