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

.NET 8中Blazor客户端提交MultipartFormDataContent到Minimal API报400错误

问题描述

我使用.NET 8创建了自动渲染模式项目,在服务端添加了如下端点:

public static void RegisterUserEndpoints(this WebApplication app)
{
    var userGroup = app.MapGroup("/api/users");

    userGroup.MapPost("/changepersonalInfo", ([FromForm] string name) =>
    {
        Results.Ok(name);
    }).RequireAuthorization();
}

并在Program.cs中注册路由:

app.RegisterUserEndpoints();
app.UseAntiforgery();

计划从客户端组件提交包含图片文件及其他字段的表单,但尝试提交简单字符串"name"时返回400 Bad Request:

var formContent = new MultipartFormDataContent();
formContent.Add(new StringContent("Sample Name"), "name");
var response = await httpClient.PostAsync("/api/users/changepersonalInfo", formContent);

注:使用[FromBody]接收JSON数据时,路由可正常工作。

补充信息:最终API将接收包含图片和其他字段的ViewModel:

userGroup.MapPost("/changepersonalInfo", ([FromForm] UserViewmodel model) =>
{
    //save image and other info
}).RequireAuthorization();

客户端提交代码示例:

var formContent = new MultipartFormDataContent();
formContent.Add(new StringContent(myProfile!.FirstName!), "firstName");
formContent.Add(new StringContent(myProfile!.LastName!), "lastName");
formContent.Add(new StringContent(myProfile!.PhoneNumber!), "phoneNumber");

// Add the image file
if (ImageFile != null)
{
    var imageContent = new ByteArrayContent(ImageBuffer);
    imageContent.Headers.ContentType = new MediaTypeHeaderValue(ImageFile.ContentType);
    imageContent.Headers.ContentDisposition = new ContentDispositionHeaderValue("form-data")
        {
            Name = "profilePicture",
            FileName = ImageFile.Name
        };
    formContent.Add(imageContent);
}

var response = await httpClient.PostAsync("/api/users/changepersonalInfo", formContent);
问题原因及解决方案

返回400 Bad Request的核心原因是缺少Antiforgery令牌。因为你启用了UseAntiforgery(),ASP.NET Core会自动验证POST/PUT/DELETE等请求的Antiforgery令牌,multipart/form-data请求也不例外,未携带令牌会直接被拒绝。

修复步骤

  1. 获取并传递Antiforgery令牌
    在客户端组件中,通过IAntiforgery服务获取令牌,然后将其添加到表单字段或请求头中:

    @inject IAntiforgery Antiforgery
    @code {
        private async Task SubmitForm()
        {
            var tokens = await Antiforgery.GetTokensAsync(HttpContext);
            var formContent = new MultipartFormDataContent();
            
            // 方式1:将令牌添加到表单字段
            formContent.Add(new StringContent(tokens.RequestToken!), "__RequestVerificationToken");
            
            // 方式2:将令牌添加到请求头
            // httpClient.DefaultRequestHeaders.Add("RequestVerificationToken", tokens.RequestToken!);
            
            // 添加其他表单字段和文件
            formContent.Add(new StringContent("Sample Name"), "name");
            // ... 图片及其他字段
            
            var response = await httpClient.PostAsync("/api/users/changepersonalInfo", formContent);
        }
    }
    
  2. ViewModel字段匹配检查
    当使用[FromForm] UserViewmodel model接收数据时,确保ViewModel的属性名称与客户端表单字段名称一致(大小写敏感)。如果需要不同的字段名,可以用[FormName]特性指定映射关系:

    public class UserViewmodel
    {
        [FormName("firstName")]
        public string FirstName { get; set; } = string.Empty;
        
        [FormName("lastName")]
        public string LastName { get; set; } = string.Empty;
        
        [FormName("phoneNumber")]
        public string PhoneNumber { get; set; } = string.Empty;
        
        public IFormFile ProfilePicture { get; set; } = null!;
    }
    
  3. 额外验证项

    • 确认请求携带了有效的Authorization凭证(JWT或Cookie),因为端点启用了RequireAuthorization(),权限验证失败也可能返回400,但当前场景下核心问题还是Antiforgery令牌缺失。
    • 无需手动设置MultipartFormDataContent的Content-Type,它会自动生成正确的类型值。

内容的提问来源于stack exchange,提问作者Robin Khan

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.30 04:53:21