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

.NET6 Blazor HttpClient Post等请求返回400 Bad Request如何解决

问题根因

.NET 6 选用Individual身份验证模板创建的Blazor Hosted项目,默认全局开启了防伪令牌(CSRF Token)校验。GET请求不涉及服务端状态修改,不会触发该校验逻辑;而POST/PUT/DELETE/PATCH这类写操作请求,必须在请求头携带服务端下发的合法防伪令牌才能通过校验,你当前的客户端请求未携带对应令牌,因此直接返回400 Bad Request响应,响应头的Content-Type: application/problem+json就是框架校验失败返回的标准问题格式。

排查验证

先做快速验证确认问题根源,不要上来就改一堆配置:
在服务端对应Post接口的Action上临时加[IgnoreAntiforgeryToken]特性跳过防伪校验:

[HttpPost]
[IgnoreAntiforgeryToken] // 仅临时测试用,生产环境禁止保留
public async Task<ActionResult> PostSomethings(AppFolder args)
{
    Console.WriteLine("Post Debug"+args);
    return await Task.Run(() => Ok());
}

重启项目重新发起请求,如果返回200 OK,就可以100%确认是防伪令牌校验导致的问题。

注意:这个方法仅用于本地测试定位问题,生产环境绝对不能全局跳过防伪校验,否则会存在严重的CSRF跨站请求伪造漏洞。

生产环境正确修复方案

1. 服务端配置(Server项目)

第一步:注册防伪服务,指定前端传递令牌的请求头名称,在Program.cs的服务注册区域添加代码:

builder.Services.AddAntiforgery(options =>
{
    options.HeaderName = "X-CSRF-TOKEN";
});

第二步:添加令牌下发中间件,在用户首次访问应用时,将防伪令牌写入允许前端读取的Cookie中。中间件要放在app.UseAuthentication()之后、app.UseAuthorization()之前:

app.UseAuthentication();

// 新增防伪令牌下发中间件
app.Use(async (context, next) =>
{
    // 仅在访问应用根路径的GET请求时下发令牌
    if (context.Request.Method == HttpMethods.Get && context.Request.Path == "/")
    {
        var antiforgery = context.RequestServices.GetRequiredService<IAntiforgery>();
        var tokens = antiforgery.GetAndStoreTokens(context);
        // 必须设置HttpOnly=false,否则前端JS无法读取Cookie内容
        context.Response.Cookies.Append("XSRF-TOKEN", 
            tokens.RequestToken ?? string.Empty, 
            new CookieOptions
            {
                HttpOnly = false,
                Secure = true,
                SameSite = SameSiteMode.Strict
            });
    }
    await next();
});

app.UseAuthorization();

2. 客户端配置(Client项目)

第一步:自定义HTTP消息处理器,自动给非GET请求附加防伪令牌头。新建AntiforgeryHandler.cs类:

public class AntiforgeryHandler : DelegatingHandler
{
    private readonly IJSRuntime _jsRuntime;
    public AntiforgeryHandler(IJSRuntime jsRuntime)
    {
        _jsRuntime = jsRuntime;
    }

    protected override async Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken)
    {
        // GET请求不需要附加令牌
        if (request.Method != HttpMethod.Get)
        {
            // 从Cookie中读取服务端下发的XSRF-TOKEN
            var token = await _jsRuntime.InvokeAsync<string>("eval", cancellationToken,
                "document.cookie.match(/(?:^|;)\\s*XSRF-TOKEN=([^;]+)/)?.[1] || ''");
            if (!string.IsNullOrEmpty(token))
            {
                request.Headers.TryAddWithoutValidation("X-CSRF-TOKEN", token);
            }
        }
        return await base.SendAsync(request, cancellationToken);
    }
}

第二步:修改Program.cs中你之前注册的public命名HttpClient,绑定自定义消息处理器:

// 替换原有AddHttpClient的注册代码
builder.Services.AddHttpClient("public", client => 
    client.BaseAddress = new Uri(builder.HostEnvironment.BaseAddress))
    .AddHttpMessageHandler<AntiforgeryHandler>();

builder.Services.AddTransient<AntiforgeryHandler>();

全部配置完成后重启项目,所有POST/PUT/DELETE/PATCH请求就可以正常通过校验,不再返回400错误。

其他可能触发400的小众场景

如果做完上述配置还是返回400,检查两个点:

  • 检查AppFolder类是否存在公共无参构造函数,所有带[Required]特性的属性是否都传了值,避免模型校验失败返回400
  • 检查请求路径是否正确,你当前代码里Post的路径是WeatherForecast,确认控制器的路由前缀和Action路由是否匹配,避免路径不匹配触发400(路径问题一般返回404,概率极低)

内容的提问来源于stack exchange,提问作者รัฐพล.James

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 08:42:15