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

ASP.Net Core 2中如何在中间件与控制器重复读取JSON请求?

解决ASP.NET Core中间件与控制器共享读取JSON请求体的问题

这个问题我之前也踩过坑,核心原因是HTTP请求体的流默认只能被读取一次——当你在中间件里读完流之后,流的位置指针已经移到末尾了,控制器再去读的时候自然就拿不到数据了。下面给你两种可行的解决方案:

方案一:修复流的可读取性(直接修改现有代码)

你当前的代码里已经用到了EnableRewind,但缺少了重置流指针的关键步骤,而且EnableRewind在ASP.NET Core 3.0+已经被标记为过时,推荐使用EnableBuffering来替代。

修改后的中间件代码

using System;
using System.IO;
using System.Text;
using System.Collections.Generic;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Newtonsoft.Json;
using srpago_oxxo.Library.Logger;
using srpago_oxxo.Library.Logger.Handlers;
using srpago_oxxo.Library.Logger.Formatters;

namespace srpago_oxxo.Middlewares
{
    public class IOMiddleware
    {
        private readonly RequestDelegate _next;
        private Logger logger;

        public IOMiddleware(RequestDelegate next)
        {
            _next = next;
            logger = new Logger("SRPAGO-OXXO");
            var formatter = new WebFormatter();
            var handler = new StdoutHandler();
            handler.SetFormatter(formatter);
            logger.AddHandler(handler);
        }

        public async Task Invoke(HttpContext context)
        {
            // 启用请求体缓冲,让流可以被多次读取
            context.Request.EnableBuffering();

            var contentLength = context.Request.ContentLength.HasValue 
                ? context.Request.ContentLength.ToString() 
                : "-";
            
            var info = new Dictionary<string, string>()
            {
                {"{code}", "200"},
                {"{clientIp}", context.User.ToString()},
                {"{method}", context.Request.Method},
                {"{path}", context.Request.Path.Value},
                {"{bodyLength}", contentLength},
                {"{message}", await GetJsonRequest(context)},
            };
            logger.Info(info);

            // 在调用下一个中间件/控制器之前,把流指针重置到开头
            context.Request.Body.Position = 0;

            await _next.Invoke(context);
        }

        private async Task<string> GetJsonRequest(HttpContext context)
        {
            var request = context.Request;
            // 读取流之前先确保指针在开头
            request.Body.Position = 0;
            
            using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true);
            var bodyAsText = await reader.ReadToEndAsync();
            
            // 读完后重置指针,确保后续组件能读到数据
            request.Body.Position = 0;

            return string.IsNullOrEmpty(bodyAsText) ? "{}" : bodyAsText;
        }
    }
}

修改控制器的GetJsonRequest方法

同样,控制器里读取流的时候也要注意重置指针,或者直接使用StreamReader的leaveOpen参数:

using System.IO;
using System.Text;
using Microsoft.AspNetCore.Mvc;

namespace srpago_oxxo.Controllers
{
    public class BaseController : Controller
    {
        protected async Task<string> GetJsonRequest()
        {
            var request = this.HttpContext.Request;
            // 重置流指针到开头
            request.Body.Position = 0;
            
            using var reader = new StreamReader(request.Body, Encoding.UTF8, leaveOpen: true);
            var body = await reader.ReadToEndAsync();
            
            // 读完后再次重置指针(如果后续还有组件需要读的话)
            request.Body.Position = 0;
            
            return body.Replace("\n", "").Replace("\t", "");
        }
    }
}

关键改动说明:

  • 使用EnableBuffering()替代EnableRewind(),它会把请求体缓存到内存或磁盘,支持多次读取
  • 每次读取流前后都重置Body.Position = 0,确保流的指针在起始位置
  • 使用StreamReader的leaveOpen: true参数,避免读取后关闭流,导致后续组件无法访问

方案二:将请求体缓存到HttpContext.Items(更优雅的方式)

如果不想每次都操作流指针,可以在中间件里把读取到的请求体内容存入HttpContext.Items,控制器直接从这里获取,避免重复读取流:

中间件修改部分

在Invoke方法里,把读取到的body存入Items:

public async Task Invoke(HttpContext context)
{
    context.Request.EnableBuffering();
    var bodyAsText = await GetJsonRequest(context);
    
    // 将请求体内容存入HttpContext.Items,供后续控制器使用
    context.Items["RequestBody"] = bodyAsText;

    var contentLength = context.Request.ContentLength.HasValue 
        ? context.Request.ContentLength.ToString() 
        : "-";
    
    var info = new Dictionary<string, string>()
    {
        {"{code}", "200"},
        {"{clientIp}", context.User.ToString()},
        {"{method}", context.Request.Method},
        {"{path}", context.Request.Path.Value},
        {"{bodyLength}", contentLength},
        {"{message}", bodyAsText},
    };
    logger.Info(info);

    await _next.Invoke(context);
}

控制器修改部分

直接从Items里取,不用再读流:

protected string GetJsonRequest()
{
    if (HttpContext.Items.TryGetValue("RequestBody", out var bodyObj) && bodyObj is string body)
    {
        return body.Replace("\n", "").Replace("\t", "");
    }
    return "{}";
}

这种方式更高效,因为只需要读取一次流,后续组件直接复用缓存的内容。

内容的提问来源于stack exchange,提问作者Dante Aguilar

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.29 08:25:20