当我们开发或调试应用程序时,检查 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 端点时,它会在我的控制台中记录以下信息:
希望这对你有帮助!祝你编码愉快!
如果您喜欢此文章,请收藏、点赞、评论,谢谢,祝您快乐每一天。