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

.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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.12 04:40:34