.NET Core 6:Action Filter中用PipeReader读RequestBody并重置流位置
在ASP.NET Core Action过滤器中读取并重置请求体的方案
核心前提:启用请求体缓冲
默认情况下,HttpRequest.Body是不可重读的流,读取一次后就会到达流末尾,后续无法再读取。所以无论用哪种方式读取,第一步必须启用请求体缓冲:
context.HttpContext.Request.EnableBuffering();
方法一:使用PipeReader读取并重置
以下是完整的IAsyncActionFilter实现,用PipeReader读取完整请求体后,重置流位置让Controller正常读取参数:
public class ReadRequestBodyWithPipeFilter : IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 启用缓冲,允许流被多次读取 context.HttpContext.Request.EnableBuffering(); // 创建PipeReader绑定请求体流 var pipeReader = PipeReader.Create(context.HttpContext.Request.Body); ReadResult readResult; // 循环读取直到获取完整请求体 do { readResult = await pipeReader.ReadAsync(); var buffer = readResult.Buffer; // 处理读取到的内容(示例:转成字符串) if (!buffer.IsEmpty) { var requestBody = Encoding.UTF8.GetString(buffer.FirstSpan); // 这里可以添加自定义逻辑,比如日志、参数校验等 } // 告知PipeReader已处理的缓冲范围 pipeReader.AdvanceTo(buffer.Start, buffer.End); } while (!readResult.IsCompleted); // 重置请求体流的位置到起始处,确保Controller能正常读取参数 context.HttpContext.Request.Body.Position = 0; // 完成PipeReader操作 await pipeReader.CompleteAsync(); // 继续执行后续过滤器与Action await next(); } }
方法二:使用StreamReader(更简洁的替代方案)
如果不需要PipeReader的高性能特性,用StreamReader实现会更简单,同样需要注意保留流不关闭:
public class ReadRequestBodyWithStreamFilter : IAsyncActionFilter { public async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { // 启用缓冲 context.HttpContext.Request.EnableBuffering(); // 读取请求体,leaveOpen: true 确保流不被StreamReader关闭 using var streamReader = new StreamReader( context.HttpContext.Request.Body, Encoding.UTF8, detectEncodingFromByteOrderMarks: false, bufferSize: 1024, leaveOpen: true); var requestBody = await streamReader.ReadToEndAsync(); // 重置流位置到起始处 context.HttpContext.Request.Body.Position = 0; // 自定义处理逻辑,比如记录请求体内容 // Console.WriteLine($"Request Body: {requestBody}"); // 继续执行流程 await next(); } }
常见问题排查
你遇到的「Body始终为空」问题,大概率是以下原因:
- 未调用
EnableBuffering(),导致第一次读取后流到达末尾,后续读取返回空 - 使用
StreamReader时未设置leaveOpen: true,流被关闭导致无法后续读取 - 请求体采用分块传输,但上述两种方法都能处理分块场景,只要启用了缓冲
注意事项
- 在
OnActionExecuted中读取请求体时,无需重置流位置(因为Controller已经完成读取),但仍需提前启用缓冲 - 如果请求体是二进制数据,需替换
Encoding.UTF8为对应的编码,或直接处理字节缓冲
内容的提问来源于stack exchange,提问作者user18928007
相关产品推荐
相关产品推荐

