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

.NET API拒绝并发multipart/form-data请求问题排查

Angular + .NET 多文件上传仅最后一个请求命中API问题排查

问题背景

用Angular + .NET实现多文件上传功能,用户选择多个文件后前端会发送多个POST请求,每个请求的form-data仅文件二进制数据不同,其他属性一致,但只有最后一个请求能成功上传,API端点仅被最后一个请求命中。前端已确认服务调用了3次,目前仅修改了Kestrel的最大请求体大小配置,疑问是否缺少并发请求相关配置。

相关代码

.NET API端点代码

[HttpPost("asset")]
[Consumes("multipart/form-data")]
[ProducesResponseType(typeof(BinaryResponse), StatusCodes.Status201Created)]
[ProducesResponseType(typeof(GenericResponse), StatusCodes.Status413RequestEntityTooLarge)]
[ProducesResponseType(typeof(GenericResponse), StatusCodes.Status400BadRequest)]
[ProducesResponseType(typeof(GenericResponse), StatusCodes.Status500InternalServerError)]
public async Task<IActionResult> UploadBinary([FromForm] IFormFile file, [FromForm] BinaryRequest binary)
{
   //...
}

Angular服务代码

uploadFile(version: string, product: string, asset: Asset) : Observable<HttpEvent<BinaryResult>> {
  const formData: FormData = new FormData();
  formData.append('version', version);
  formData.append('file', asset.file);
  formData.append('operationalSystem', asset.operationalSystem);
  formData.append('architecture', asset.architecture);
  
  return super.postReporter<BinaryResult>('asset', formData);
}

//Base class:
protected postReporter<X>(sub: string, content: any) {
  return this.httpClient.post<X>(`${environment.apiUrl}/${this.base}/${sub}`, content, {
    reportProgress: true,
    observe: 'events',
  });
}

已配置的Kestrel参数

builder.Services.Configure<KestrelServerOptions>(options =>
{
    options.Limits.MaxRequestBodySize = int.MaxValue;
});

问题排查与解决方案

首先明确:.NET默认支持并发请求,不需要额外配置开启基础并发能力,所以问题大概率不在并发配置本身,从以下方向排查:

1. 前端请求是否被意外取消/覆盖

这是最常见的原因:

  • 检查前端调用uploadFile的方式:如果用了switchMap这类操作符,每次新请求会取消前序未完成的请求,最终只有最后一个请求能完成;或者循环调用时未正确订阅每个请求,导致前序请求被垃圾回收或取消。
  • 验证:打开浏览器开发者工具的Network面板,查看前两个请求的状态——如果是canceled或aborted,说明请求被取消了。
  • 修复:
    • 若使用RxJS操作符,将switchMap替换为concatMap(串行执行)或forkJoin(并行执行);
    • 批量上传时,用forkJoin统一处理多个请求:
      uploadMultiple(version: string, product: string, assets: Asset[]): Observable<BinaryResult[]> {
        const requestList = assets.map(asset => 
          this.uploadFile(version, product, asset).pipe(
            filter(e => e.type === HttpEventType.Response),
            map(e => e.body as BinaryResult)
          )
        );
        return forkJoin(requestList);
      }
      

2. 补充ASP.NET Core请求大小限制配置

仅配置Kestrel的MaxRequestBodySize不够,ASP.NET Core本身还有表单请求的限制:

  • 在Program.cs中添加全局表单配置:
    builder.Services.Configure<FormOptions>(options =>
    {
        options.MultipartBodyLengthLimit = long.MaxValue; // 针对multipart/form-data的大小限制
        options.ValueLengthLimit = int.MaxValue;
        options.KeyLengthLimit = int.MaxValue;
    });
    
  • 同时在控制器方法上添加特性增强限制:
    [HttpPost("asset")]
    [Consumes("multipart/form-data")]
    [RequestSizeLimit(long.MaxValue)]
    [RequestFormLimits(MultipartBodyLengthLimit = long.MaxValue)]
    // 其他ProducesResponseType...
    public async Task<IActionResult> UploadBinary([FromForm] IFormFile file, [FromForm] BinaryRequest binary)
    {
       //...
    }
    

3. 排查后端代码的线程安全问题

检查UploadBinary方法内部逻辑:如果使用了静态变量、共享资源且未做线程同步,并发请求可能导致前序请求处理异常但未抛出错误,看起来像未命中端点。比如共享的文件路径、缓存对象被覆盖等。

总结

优先排查前端请求是否被取消,这是此类问题的核心诱因;其次完善ASP.NET Core的请求大小限制配置;最后检查后端代码的线程安全问题。.NET默认支持并发请求,无需额外配置开启基础并发。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.08 23:57:13