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

.Net Core中间件记录Bad Request时读取请求体为空怎么解决

问题根因
  • 核心问题是请求体缓冲开启时机错误:你当前在await _next(context)执行完成后才调用context.Request.EnableBuffering(),此时ASP.NET Core内置的模型绑定、验证中间件已经读取过请求体,默认配置下请求体读取后不会保留内容,流指针处于末尾,因此读取结果为空。
  • 次要问题:要提取400响应的错误列表,需要提前对响应体做缓存处理,默认响应体是只进流,发送到客户端后无法读取内容。
修复实现

调整中间件逻辑

修改后的完整中间件逻辑如下,包含提前开启缓冲、读写请求响应内容、解析字段调用日志的完整流程:

using InfiniteDocumentsAPI.Core.Services;
using Microsoft.AspNetCore.Http;
using System;
using System.IO;
using System.Text;
using System.Text.Json;
using System.Threading.Tasks;

namespace MBFSPT_OCRABBYY_InfiniteDocumentsAPI.Middlewares
{
    public class RequestLoggingMiddleware
    {
        private readonly RequestDelegate _next;

        public RequestLoggingMiddleware(RequestDelegate next)
        {
            _next = next;
        }

        public async Task Invoke(HttpContext context, LoggerService log)
        {
            // 提前开启请求体缓冲,允许重复读取
            context.Request.EnableBuffering();
            
            // 缓存原始响应流,替换为可重读的内存流
            var originalResponseBody = context.Response.Body;
            using var responseMemoryStream = new MemoryStream();
            context.Response.Body = responseMemoryStream;

            try
            {
                await _next(context);
            }
            finally
            {
                if (context.Response.StatusCode == 400)
                {            
                    string requestStr = string.Empty;
                    string responseStr = string.Empty;

                    try
                    {
                        // 读取请求体
                        context.Request.Body.Position = 0;
                        using (var reader = new StreamReader(
                            context.Request.Body, 
                            encoding: Encoding.UTF8, 
                            detectEncodingFromByteOrderMarks: false, 
                            leaveOpen: true))
                        {
                            requestStr = await reader.ReadToEndAsync();
                        }
                        context.Request.Body.Position = 0;

                        // 读取响应体
                        responseMemoryStream.Position = 0;
                        using (var reader = new StreamReader(
                            responseMemoryStream, 
                            Encoding.UTF8, 
                            detectEncodingFromByteOrderMarks: false, 
                            leaveOpen: true))
                        {
                            responseStr = await reader.ReadToEndAsync();
                        }

                        // 解析字段调用日志
                        if (!string.IsNullOrEmpty(requestStr) && !string.IsNullOrEmpty(responseStr))
                        {
                            using var requestDoc = JsonDocument.Parse(requestStr);
                            var appInfo = requestDoc.RootElement.GetProperty("applicationInfo");
                            string applicationId = appInfo.GetProperty("applicationId").GetInt32().ToString();
                            string applicationNumber = appInfo.GetProperty("applicationNumber").GetString() ?? string.Empty;
                            string applicationStatusLocalText = appInfo.GetProperty("applicationStatusLocalText").GetString() ?? string.Empty;

                            using var responseDoc = JsonDocument.Parse(responseStr);
                            var errors = responseDoc.RootElement.GetProperty("errors");
                            string stringfiedResponsesErrorList = errors.GetRawText();

                            log.Trace($"BatchNotCreated{stringfiedResponsesErrorList}", applicationId, applicationNumber, 0, applicationStatusLocalText);
                        }
                    }
                    catch
                    {
                        // 可自行添加解析异常的兜底处理逻辑
                    }
                    finally
                    {
                        // 将响应内容写回原始流,保证客户端正常收到响应
                        responseMemoryStream.Position = 0;
                        await responseMemoryStream.CopyToAsync(originalResponseBody);
                        context.Response.Body = originalResponseBody;
                    }
                }
                else
                {
                    // 非400状态码直接返回响应
                    responseMemoryStream.Position = 0;
                    await responseMemoryStream.CopyToAsync(originalResponseBody);
                    context.Response.Body = originalResponseBody;
                }
            }
        }     
    }
}

注意事项

  • 确保中间件注册顺序在路由、认证、模型验证相关中间件之前,在Program.cs中app.UseMiddleware<RequestLoggingMiddleware>()的调用位置需放在app.MapControllers()之前。
  • 如果项目使用Newtonsoft.Json作为序列化组件,将代码中JsonDocument相关逻辑替换为JObject.Parse即可,解析逻辑保持一致。

内容的提问来源于stack exchange,提问作者Cagri Tacyildiz

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.14 11:36:06