【问题标题】:Not receiving all response headers in middleware未在中间件中接收所有响应标头
【发布时间】:2021-05-03 19:35:34
【问题描述】:

我目前正在 .NET 5 中开发 API,需要向 API 添加请求和响应日志记录。我能够轻松记录请求正文和标头。我也能够毫无问题地记录响应正文。出现的问题是当我想记录响应标头时。我目前只能访问Content-LengthContent-Type

在 Postman 中查找我收到的请求的响应时:

  • Date
  • Content-Type
  • Server
  • Content-Length
  • X-Rate-Limit-Limit
  • X-Rate-Limit-Remaining
  • X-Rate-Limit-Reset

X-Rate-Limit- 是应用程序中的中间件。我试图在Startup.cs 中的这个中间件的上方和下方添加我的中间件,其中没有一个返回这些标头。我有一种感觉,这就是创建请求时我在中间件中管理响应对象的方式。

这是我的中间件的样子:

namespace X.API.Middleware
{
    public class RequestResponseLoggingMiddleware
    {
        /// <summary>
        /// Request delegate that allows us to move the middleware along
        /// </summary>
        private readonly RequestDelegate _next;

        /// <summary>
        /// Constructor for DI
        /// </summary>
        /// <param name="next"></param>
        public RequestResponseLoggingMiddleware(RequestDelegate next)
        {
            _next = next;
        }
        
        public async Task Invoke(HttpContext context)
        {
            //Enable buffering so that the streams can be read
            context.Request.EnableBuffering();

            //Read the request body to a string for logging
            var requestBodyString = await new StreamReader(context.Request.Body).ReadToEndAsync();
            //Read the request headers
            var requestHeaders = context.Request.Headers.ToDictionary(a => a.Key, a => a.Value);
            
            //Log the incoming request
            LogContext.PushProperty("RequestBody", requestBodyString);
            LogContext.PushProperty("RequestHeaders", requestHeaders);
            Log.Information("Incoming Request");

            //Reset the position of the request body so it doesn't seem read
            context.Request.Body.Position = 0;

            //Create a new body to be used
            var originalBody = context.Response.Body;
            await using var newBody = new MemoryStream();
            context.Response.Body = newBody;
            
            //Calls the next middleware
            await _next(context);
            
            //Seeks the body back
            newBody.Seek(0, SeekOrigin.Begin);
            //Read the response body string
            var responseBodyString = await new StreamReader(context.Response.Body).ReadToEndAsync();
            //Read the response headers
            var responseHeaders = context.Response.Headers.ToDictionary(a => a.Key, a => a.Value);

            //Check if the URL is not a documentation URL
            if (!context.Request.Path.ToString().Contains("api-docs") && !context.Request.Path.ToString().Contains("swagger"))
            {
                //Reset custom props
                LogContext.Reset();
                //Log the response
                LogContext.PushProperty("ResponseBody", responseBodyString);
                LogContext.PushProperty("ResponseHeaders", responseHeaders);
                Log.Information("Outgoing Response");
            }
            
            //Return the original body
            newBody.Seek(0, SeekOrigin.Begin);
            await newBody.CopyToAsync(originalBody);
        }
    }
}

Startup.cs中的中间件注册如下:

public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ApiContext context) {

    app.UseRequestResponseLogging();

    //Enable rate limiting
    app.UseIpRateLimiting();

    app.UseExceptionHandler(errorApp => errorApp.Run(ExceptionHandler.HandleException()));

    app.UseSwagger();

    app.UseReDoc(c => {
        c.DocumentTitle = "Curo API Documentation";
        c.SpecUrl = "/swagger/v1/swagger.json";
    });

    app.UseHttpsRedirection();

    app.UseRouting();

    app.UseAuthorization();

    app.UseEndpoints(endpoints => {
        endpoints.MapControllers();
    });

    Log.Information("Application started!");
}

有问题的中间件是UseRequestResponseLogging。我尝试将中间件放在顶部和底部,它们之间没有区别,因为我相信到那时请求和响应已经设置好了。

【问题讨论】:

    标签: c# asp.net-core asp.net-core-webapi


    【解决方案1】:

    我找到了一种方法,方法是连接到 Response.OnStarting。我可以访问那里的所有标题;我相信这是在将最终标头发送给客户端之前调用的最后一个事件/挂钩。代码如下:

    context.Response.OnStarting((state) => {
        var headers = context.Response.Headers.ToDictionary(a => a.Key, a => a.Value);
        return Task.CompletedTask;
    }, null!);
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2021-03-17
      • 2014-03-05
      • 2011-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2017-07-05
      • 1970-01-01
      相关资源
      最近更新 更多