minimal api需手动解析multipart body,通过request.form.files获取文件,先调用readformasync()再访问form,校验大小、路径遍历、文件头,用openreadstream流式处理。

Minimal API里怎么接收上传的文件
Minimal API不支持传统MVC的IFormFile绑定,直接写HttpRequest参数会报错:「No parameterless constructor defined for type 'Microsoft.AspNetCore.Http.IFormFile'」。必须手动解析multipart body。
- 用
Request.Form.Files获取文件集合——这是最直接、兼容.NET 6/7/8的方式 - 确保前端用
multipart/form-data提交,且<input type="file">有name="file"(或对应名称) - 不要试图给参数加
[FromForm]或[AsParameters]——Minimal API不认这些特性 - 如果只传一个文件,取
Request.Form.Files[0];多个就遍历Files集合
保存文件前必须检查什么
跳过校验直接CopyToAsync是线上事故高发点。常见错误现象:上传超大文件卡死进程、恶意扩展名绕过、临时目录写满。
- 用
Request.ContentLength做初步大小拦截,比如限制≤20MB:if (Request.ContentLength > 20 * 1024 * 1024) throw new InvalidOperationException("File too large"); - 检查
file.FileName是否含路径遍历字符(如../),否则可能被写到任意目录 - 别信
file.ContentType——它由浏览器提供,可伪造;真正要校验的是文件头(magic bytes) - 生成安全文件名:用
Path.GetRandomFileName()+ 原扩展名(需白名单过滤,如只允许.jpg、.pdf)
同步写磁盘还是流式处理
直接CopyToAsync到本地磁盘最简单,但遇到大文件或高并发时,I/O阻塞和磁盘争用会让吞吐骤降。.NET 6+推荐流式处理,尤其要转存到Blob、压缩、或加水印时。
- 用
using var stream = file.OpenReadStream()拿到原始流,避免一次性加载进内存 - 若需多阶段处理(如先解压再校验),保持流打开,用
stream.Position = 0重置位置,别反复OpenReadStream() - 别在Minimal API里用
File.WriteAllBytes——它是同步阻塞调用,会拖垮线程池 - 临时文件慎用
Path.GetTempPath():容器环境可能无写权限,建议显式配置appsettings.json里的Upload:TempDir
为什么Request.Form有时是null
现象:调试时Request.Form为null,但Postman里确认Content-Type是multipart/form-data。根本原因是请求体未被读取,ASP.NET Core默认延迟解析form data。
- 必须在访问
Request.Form前,先调用await Request.ReadFormAsync()(.NET 6)或await Request.Form.ReadFormAsync()(.NET 7/8) - .NET 7起
Request.Form是懒加载,首次访问会自动触发读取;但.NET 6必须显式调用,否则就是null - 如果同时要读
Request.Query和Request.Form,优先读Form——因为读取body只能一次,读完Query可能失效 - 别在中间件里提前读
Request.Body(比如日志中间件),那会清空流,导致后续Form读不到
文件名解析、流位置控制、form读取时机——这三处最容易在压测或灰度时突然出问题,不是代码写不对,而是边界条件没覆盖到。










