.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); }
- 若使用RxJS操作符,将
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
相关产品推荐
相关产品推荐

