在 .NET Web API 中记录 HTTP 请求和响应

        当我们开发或调试应用程序时,检查 HTTP 请求和响应的内容有助于我们修复一些错误,甚至在关键环境(例如生产环境)中防止出现错误。

        然后,本文将介绍如何记录来自 Web API 的 HTTP 请求和响应的内容。我的目标是展示两种方法:第一种方法可应用于 .NET Core 和 .NET 5,第二种方法可应用于 .NET 6。

        请注意,这两种方法都将介绍如何记录 HTTP 请求和响应的标头和正文,然后小心在生产环境中记录敏感数据。 

1. 创建用于测试的控制器
        为了测试以下方法,我创建了一个简单的控制器。这里的主要目标是拥有一个将被记录的小主体和标题。 

logging-controller.cs: 

using Microsoft.AspNetCore.Mvc;

namespace HttpLogging.Controllers;

[ApiController]
[Route("[controller]")]
public class LoggingController : ControllerBase
{
    [HttpPost]
    public IActionResult Post([FromBody]KeyValueRequest request)
    {
        Response.Headers.Add("my-response-header", "My response header");
        return Ok(request);
    }
}

        此控制器有一个 POST 方法,它将接收KeyValueRequest类型的请求对象,并将相同的对象返回给消费者。此外,此时正在创建一个新的标头响应,即my-response-header。

请求对象包含一个属性Items。此属性是Dictionary<string, string>类型:

logging-request-model.cs:

namespace HttpLogging.Controllers;

public class KeyValueRequest
{
    public Dictionary<string, string> Items { get; set; }
}

2. 使用自定义中间件应用 HTTP 日志记录:
现在让我们看看记录来自 HTTP 请求和 HTTP 响应的任何数据的第一种方法。

        为此,我创建了一个名为LogginMiddleware的新中间件。该中间件将包含两种从 HTTP 请求和响应中获取数据的方法:

        LogRequest方法(下面代码红色部分):读取HTTP请求头和正文,然后将所有值附加到StringBuilder对象中。附加所有值后,将通过Log.LogInformation方法(下面代码红色部分)记录内容。
LogResponse方法(下面代码红色部分):该方法与LogRequest方法类似,但不是从 HTTP 请求标头和正文中获取数据,而是从 HTTP 响应中获取数据。

logging-middleware.cs: 

using System.Text;

namespace HttpLogging.Middleware;

public class LoggingMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILogger<LoggingMiddleware> _logger;

    public LoggingMiddleware(RequestDelegate next, ILogger<LoggingMiddleware> logger)
    {
        _next = next;
        _logger = logger;
    }

    public async Task Invoke(HttpContext context)
    {
        await LogRequest(context);

        var originalResponseBody = context.Response.Body;

        using (var responseBody = new MemoryStream())
        {
            context.Response.Body = responseBody;
            await _next.Invoke(context);
            
            await LogResponse(context, responseBody, originalResponseBody);
        }
    }

    private async Task LogResponse(HttpContext context, MemoryStream responseBody, Stream originalResponseBody)
    {
        var responseContent = new StringBuilder();
        responseContent.AppendLine("=== Response Info ===");
        
        responseContent.AppendLine("-- headers");
        foreach (var (headerKey, headerValue) in context.Response.Headers)
        {
            responseContent.AppendLine($"header = {headerKey}    value = {headerValue}");
        }

        responseContent.AppendLine("-- body");
        responseBody.Position = 0;
        var content = await new StreamReader(responseBody).ReadToEndAsync();
        responseContent.AppendLine($"body = {content}");
        responseBody.Position = 0;
        await responseBody.CopyToAsync(originalResponseBody);
        context.Response.Body = originalResponseBody;

        _logger.LogInformation(responseContent.ToString());
    }

    private async Task LogRequest(HttpContext context)
    {
        var requestContent = new StringBuilder();

        requestContent.AppendLine("=== Request Info ===");
        requestContent.AppendLine($"method = {context.Request.Method.ToUpper()}");
        requestContent.AppendLine($"path = {context.Request.Path}");

        requestContent.AppendLine("-- headers");
        foreach (var (headerKey, headerValue) in context.Request.Headers)
        {
            requestContent.AppendLine($"header = {headerKey}    value = {headerValue}");
        }

        requestContent.AppendLine("-- body");
        context.Request.EnableBuffering();
        var requestReader = new StreamReader(context.Request.Body);
        var content = await requestReader.ReadToEndAsync();
        requestContent.AppendLine($"body = {content}");

        _logger.LogInformation(requestContent.ToString());
        context.Request.Body.Position = 0;
    }
}

        Request 和 Response 对象的 Body 属性都是Stream类型。在读取其内容之前和之后,我需要将流光标移动到开头 —检查上面代码中的绿色部分

当我运行 Web API 并调用 POST 端点时,它会在我的控制台中记录以下信息:

3.使用 .NET 6 中间件应用 HTTP 日志记录:
        对于在 .NET 6 中创建的 Web API,我们可以使用之前介绍的相同方法。但是,当我们需要记录 HTTP 请求和响应数据时,.NET 6 使我们的工作更加轻松。我们只需应用以下简单步骤即可。

        首先,在appSettings.json文件的Logging.LogLevel部分中添加一个新属性(下面代码中的第 6 行):Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware。

此属性提供 Web API 记录 HTTP 请求和响应数据的日志级别。

logging-middleware-settings.json:

{
  "Logging": {
    "LogLevel": {
      "Default": "Information",
      "Microsoft.AspNetCore": "Warning",
      "Microsoft.AspNetCore.HttpLogging.HttpLoggingMiddleware": "Information"
    }
  }
}

在 Program 类中,添加HTTP Logging 服务(下面代码中的红色部分),它是Microsoft.AspNetCore.HttpLogging命名空间的一部分。

logging-http-logging.cs:

using Microsoft.AspNetCore.HttpLogging;

var builder = WebApplication.CreateBuilder(args);

// Add services to the container.

builder.Services.AddControllers();
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();

builder.Services.AddHttpLogging(logging =>
{
    // Customize HTTP logging here.
    logging.LoggingFields = HttpLoggingFields.All;
    logging.RequestHeaders.Add("sec-ch-ua");
    logging.ResponseHeaders.Add("my-response-header");
    logging.MediaTypeOptions.AddText("application/javascript");
    logging.RequestBodyLogLimit = 4096;
    logging.ResponseBodyLogLimit = 4096;
});

var app = builder.Build();

// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
    app.UseSwagger();
    app.UseSwaggerUI();
}

app.UseHttpLogging();

app.UseHttpsRedirection();

app.UseAuthorization();

app.MapControllers();

app.Run();

        添加此服务时,您可以定义一些配置来记录 HTTP 请求和响应数据。在此示例中,我定义了以下属性:

HttpLoggingFields.All(红色部分):记录来自 HTTP 请求和响应的每个数据。
RequestHeaders(红色部分):定义允许记录哪些请求标头。如果不提供标头名称,日志中某些标头将显示“Redacted”字样。
ResponseHeaders(红色部分):定义允许记录哪些响应标头。
MediaTypeOptions(红色部分):为特定媒体类型配置编码。
RequestBodyLogLimit(红色部分):要记录的最大请求正文大小。
ResponseBodyLogLimit(红色部分):要记录的最大响应主体大小。
添加HTTP 日志服务并设置其属性后,我添加了一个新的中间件来记录 HTTP 请求和响应(app.UseHttpLogging();)。

当我运行 Web API 并调用 POST 端点时,它会在我的控制台中记录以下信息:

希望这对你有帮助!祝你编码愉快!

如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

csdn_aspnet

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值