首页 > 解决方案 > 将标头请求和标头响应添加到 serilog 中间件日志记录

问题描述

我将 serilog 日志记录添加到我的 .net 核心 Web api,但我想扩展我当前的中间件以显示标头请求。这是我的中间件类:

internal class SerilogRequestLoggerMiddleware
{
    readonly RequestDelegate _next;

    public SerilogRequestLoggerMiddleware(RequestDelegate next)
    {
        if (next == null) throw new ArgumentNullException(nameof(next));
        _next = next;
    }

    public async Task Invoke(HttpContext httpContext)
    {
        if (httpContext == null) throw new ArgumentNullException(nameof(httpContext));

        // Push the user name into the log context so that it is included in all log entries
        LogContext.PushProperty("UserName", httpContext.User.Identity.Name);

        // Getting the request body is a little tricky because it's a stream
        // So, we need to read the stream and then rewind it back to the beginning
        string requestBody = "";
        HttpRequestRewindExtensions.EnableBuffering(httpContext.Request);
        Stream body = httpContext.Request.Body;
        byte[] buffer = new byte[Convert.ToInt32(httpContext.Request.ContentLength)];
        await httpContext.Request.Body.ReadAsync(buffer, 0, buffer.Length);
        requestBody = Encoding.UTF8.GetString(buffer);
        body.Seek(0, SeekOrigin.Begin);
           


        httpContext.Request.Body = body;

        Log.ForContext("RequestHeaders", httpContext.Request.Headers.ToDictionary(h => h.Key, h => h.Value.ToString()), destructureObjects: true)
            .ForContext("RequestBody", requestBody)
            .Debug("Request information {RequestMethod} {RequestPath} information", httpContext.Request.Method, httpContext.Request.Path);
        Log.Information(string.Format("Request Header: {0} ", requestBody));
        Log.Information(string.Format("Request Body: {0} ", requestBody));
        // The reponse body is also a stream so we need to:
        // - hold a reference to the original response body stream
        // - re-point the response body to a new memory stream
        // - read the response body after the request is handled into our memory stream
        // - copy the response in the memory stream out to the original response stream
        using (var responseBodyMemoryStream = new MemoryStream())
        {
            var originalResponseBodyReference = httpContext.Response.Body;
            httpContext.Response.Body = responseBodyMemoryStream;

            //await _next(httpContext);

            try
            {
                await _next(httpContext);
            }
            catch (Exception exception)
            {
                Guid errorId = Guid.NewGuid();
                Log.ForContext("Type", "Error")
                    .ForContext("Exception", exception, destructureObjects: true)
                    .Error(exception, exception.Message + ". {@errorId}", errorId);

                var result = JsonConvert.SerializeObject(new { error = exception.Message, errorId = errorId });
                httpContext.Response.ContentType = "application/json";
                httpContext.Response.StatusCode = 500;
                await httpContext.Response.WriteAsync(result);
            }

            httpContext.Response.Body.Seek(0, SeekOrigin.Begin);
            var responseBody = await new StreamReader(httpContext.Response.Body).ReadToEndAsync();
            httpContext.Response.Body.Seek(0, SeekOrigin.Begin);

            Log.ForContext("RequestBody", requestBody)
                .ForContext("ResponseBody", responseBody)
                .Debug("Response information {RequestMethod} {RequestPath} {statusCode}", httpContext.Request.Method, httpContext.Request.Path, httpContext.Response.StatusCode);

            await responseBodyMemoryStream.CopyToAsync(originalResponseBodyReference);
        }
    }
}

我尝试过这样的事情

    //byte[] bufferHeader = new byte[Convert.ToInt32(httpContext.Request.Headers.ContentLength)];
    var header1 = httpContext.Request.Headers;
    var bn = header1 .Values.ToList();

我得到了一些 20 个不同的对象,这些对象在我的初始 api 调用中没有出现(cors、localhost 地址、gz、我不熟悉的东西..)

有人可以在这里提出问题,记录api方法的标头请求/响应的正确方法是什么,最好是通过中间件?

标签: c#asp.net-coreasp.net-core-webapiserilog

解决方案


我相信您知道要记录的标题键,对吗?

如果是这样,您可以使用:

  • .TryGetValue(TKey key, out TValue value)- 如果请求具有这样的标头,则返回 true,并将值设置为 out 变量。下面的例子:
bool headerExists = httpContext.Request.Headers.TryGetValue("header-key", out var headerValue);
  • .this[string key]- 如果字典有键,这将获取标题值。但是,如果密钥不存在,它会引发异常。下面的例子:
var headerValue = HttpContext.Request.Headers["header-key"];

另一方面,如果您有一个不想记录的标题列表,那么我认为您必须获取所有标题并排除这些标题。

推荐阅读