.NET 5使用MultipartReader提示流已被其他组件读取异常
.NET 5 MVC流式大文件上传报流意外结束错误修复
问题表现
实现大文件流式上传功能时,执行reader.ReadNextSectionAsync()方法抛出异常:
Unexpected end of Stream, the content may have already been read by another component.(流意外结束,内容可能已被其他组件读取)
已实现DisableFormValueModelBinding过滤器移除表单相关值提供器,问题仍未解决。
对应上传接口代码
[HttpPost] [DisableFormValueModelBinding] [ValidateAntiForgeryToken] public async Task<IActionResult> UploadPhysical() { if (!MultipartRequestHelper.IsMultipartContentType(Request.ContentType)) { ModelState.AddModelError("File", $"The request couldn't be processed (Error 1)."); return BadRequest(ModelState); } var boundary = MultipartRequestHelper.GetBoundary( MediaTypeHeaderValue.Parse(Request.ContentType), _defaultFormOptions.MultipartBoundaryLengthLimit); var reader = new MultipartReader(boundary, HttpContext.Request.Body); var section = await reader.ReadNextSectionAsync();
现有DisableFormValueModelBinding过滤器代码
public void OnResourceExecuting(ResourceExecutingContext context) { var factories = context.ValueProviderFactories; factories.RemoveType<FormValueProviderFactory>(); factories.RemoveType<FormFileValueProviderFactory>(); factories.RemoveType<JQueryFormValueProviderFactory>(); }
运行环境:.NET 5.0,Razor + MVC控制器架构。
根因
请求体流在进入接口业务逻辑前已经被其他组件读取完毕,流指针位于末尾,MultipartReader无法读取到有效分段。绝大多数同类问题由以下两点导致:
[ValidateAntiForgeryToken]默认从请求表单中提取防伪令牌,该逻辑执行优先级高于接口业务代码,会完整消费请求体流。- 全局中间件/过滤器(如请求日志、全局参数校验组件)提前读取了
Request.Form或Request.Body,读取后未重置流位置。
修复步骤
- 调整防伪令牌读取逻辑,避免触发请求体读取
在服务配置阶段指定防伪令牌从请求头读取,不再从表单字段解析:
前端发起上传请求时,将防伪令牌值写入services.AddAntiforgery(options => { options.HeaderName = "X-CSRF-TOKEN"; });X-CSRF-TOKEN请求头,不要作为表单字段提交。 - 补全DisableFormValueModelBinding过滤器逻辑
现有代码仅移除了值提供器工厂,未阻止框架自动读取表单,需要补充逻辑覆盖默认表单特性:// 先通过构造函数注入IOptions<FormOptions>获取_defaultFormOptions private readonly FormOptions _defaultFormOptions; public DisableFormValueModelBindingAttribute(IOptions<FormOptions> defaultFormOptions) { _defaultFormOptions = defaultFormOptions.Value; } public void OnResourceExecuting(ResourceExecutingContext context) { var factories = context.ValueProviderFactories; factories.RemoveType<FormValueProviderFactory>(); factories.RemoveType<FormFileValueProviderFactory>(); factories.RemoveType<JQueryFormValueProviderFactory>(); // 新增:重置表单特性,阻止框架自动解析表单 context.HttpContext.Features.Set<IFormFeature>( new FormFeature(context.HttpContext.Request, _defaultFormOptions) ); } - 排查全局组件
检查所有全局注册的中间件、Action过滤器,若存在读取Request.Body或Request.Form的逻辑,要么移除相关逻辑,要么在读取前调用Request.EnableBuffering()开启请求缓冲,读取完成后将Request.Body.Position重置为0,保证后续组件可以正常读取流。
内容的提问来源于stack exchange,提问作者Olof84
相关产品推荐
相关产品推荐

