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

ASP.NET Core 5客户端提交带文件对象到API失败解决方法

核心错误点
  • 客户端使用application/json格式序列化发送包含IFormFile的对象,JSON格式无法承载二进制文件内容,与API端[FromForm]要求的multipart/form-data表单格式不匹配,是请求失败的直接原因。
  • 代码中混用async/await与阻塞式.Result调用,ASP.NET Core上下文下易触发线程死锁。
  • 每次请求新建HttpClient实例存在套接字耗尽风险,生产环境需调整。

可正常运行的实现代码

1. 模型定义

原有模型定义无需修改,可直接使用:

public class ABC
{
    public int Id { get; set; }
    public string Image { get; set; }
    public string Name { get; set; }
    [NotMapped]
    public IFormFile File { get; set; }
}

注意:客户端上传页面的表单必须添加enctype="multipart/form-data"属性,否则客户端控制器无法接收到前端上传的文件,表单参考示例:

<form asp-action="Create" enctype="multipart/form-data" method="post">
    @Html.AntiForgeryToken()
    <div>
        <label>名称</label>
        <input asp-for="Name" />
    </div>
    <div>
        <label>图片</label>
        <input asp-for="File" type="file" />
    </div>
    <button type="submit">提交</button>
</form>

2. 客户端控制器修正代码

改用MultipartFormDataContent构造表单请求,全程使用异步调用避免死锁:

[HttpPost]
[ValidateAntiForgeryToken]
public async Task<IActionResult> Create(ABC aBC)
{
    // 生产环境建议使用IHttpClientFactory创建HttpClient复用实例,此处为简化演示直接实例化
    using var httpClient = new HttpClient();
    using var formContent = new MultipartFormDataContent();
    
    // 拼接普通业务字段
    formContent.Add(new StringContent(aBC.Name ?? string.Empty, Encoding.UTF8), nameof(ABC.Name));
    formContent.Add(new StringContent(aBC.Image ?? string.Empty, Encoding.UTF8), nameof(ABC.Image));

    // 拼接上传文件
    if (aBC.File != null && aBC.File.Length > 0)
    {
        var fileContent = new StreamContent(aBC.File.OpenReadStream());
        fileContent.Headers.ContentType = new System.Net.Http.Headers.MediaTypeHeaderValue(aBC.File.ContentType);
        formContent.Add(fileContent, nameof(ABC.File), aBC.File.FileName);
    }

    // 异步发送请求
    var response = await httpClient.PostAsync($"{BaseURLManager.BaseUrl}ABCs", formContent);
    response.EnsureSuccessStatusCode();
    var responseStr = await response.Content.ReadAsStringAsync();
    var result = JsonConvert.DeserializeObject<ABC>(responseStr);
    
    return View(result);
}

3. API端控制器代码

补充请求类型标注、文件存储逻辑,原有绑定逻辑可正常工作:

// POST: api/ABCs
[HttpPost]
[Consumes("multipart/form-data")] // 明确指定接口接收表单类型请求
public async Task<ActionResult<ABC>> PostABC([FromForm] ABC aBC)
{
    // 处理文件存储:示例为保存到服务端wwwroot/uploads目录,可按需替换为云存储逻辑
    if (aBC.File != null && aBC.File.Length > 0)
    {
        var uploadDir = Path.Combine(Directory.GetCurrentDirectory(), "wwwroot", "uploads");
        if (!Directory.Exists(uploadDir)) Directory.CreateDirectory(uploadDir);
        
        // 建议对文件重命名(如加GUID前缀)避免同名覆盖,此处简化演示直接用原文件名
        var saveFileName = Path.GetFileName(aBC.File.FileName);
        var savePath = Path.Combine(uploadDir, saveFileName);
        using var fs = new FileStream(savePath, FileMode.Create);
        await aBC.File.CopyToAsync(fs);

        // 将文件访问路径赋值给Image字段存入数据库
        aBC.Image = $"/uploads/{saveFileName}";
    }

    _context.ABC.Add(aBC);
    await _context.SaveChangesAsync();

    return CreatedAtAction("GetABC", new { id = aBC.Id }, aBC);
}

如果需要调整允许上传的文件大小限制,可在项目Startup.cs的ConfigureServices方法中添加配置:

services.Configure<FormOptions>(options =>
{
    options.MultipartBodyLengthLimit = 1024 * 1024 * 100; // 最大允许100MB文件,可按需调整
});

额外注意事项

  • 生产环境不要每次请求新建HttpClient,请通过IHttpClientFactory获取实例复用,避免套接字耗尽问题。
  • 正式环境必须增加文件类型校验、文件名校验逻辑,防止恶意脚本文件上传攻击。
  • 若客户端与API部署在不同域名下,需在API端配置CORS策略允许客户端跨域请求。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 03:48:22