在 ASP.NET Core 6 中該如何紀錄每一個 API 的請求(Request)和回應(Response)資訊呢?很簡單,就是使用中介軟體(Middleware),很容易的就能夠攔截到 API 的資訊,再來就只是如何儲存了。
建立攔截的中介軟體
使用中介軟體
測試用的範例 API:
範例輸出:
目前是通通寫在 log 中,這樣所有 API 請求回應和一般的 log 都混在一起,要除錯有夠麻煩,該怎麼辦?
那可以參考看看下方的連結,是昨天的文章,介紹使用 Serilog 套件輸出 log 時該如何將內容拆分為多個檔案。
延伸閱讀:C# ASP.NET Core 6 依照功能拆分 Serilog 套件輸出的 log 檔案
建立攔截的中介軟體
public class ApiLoggingMiddleware
{
private readonly ILogger<ApiLoggingMiddleware> _logger;
private readonly RequestDelegate _next;
public ApiLoggingMiddleware(ILogger<ApiLoggingMiddleware> logger, RequestDelegate next)
{
_logger = logger;
_next = next;
}
public async Task InvokeAsync(HttpContext context)
{
// 攔截請求
var request = await FormatRequest(context.Request);
// 紀錄回應前的狀態
var originalBodyStream = context.Response.Body;
using var responseBody = new MemoryStream();
context.Response.Body = responseBody;
await _next(context);
// 攔截回應
var response = await FormatResponse(context.Response);
// 紀錄資訊
_logger.LogInformation("Request: \n{Request}", request);
_logger.LogInformation("Response: \n{Response}", response);
// 將原始回應內容寫回
await responseBody.CopyToAsync(originalBodyStream);
}
/// <summary>
/// 格式化請求
/// </summary>
private async Task<string> FormatRequest(HttpRequest request)
{
request.EnableBuffering();
var headers = FormatHeaders(request.Headers);
var body = await new StreamReader(request.Body, Encoding.UTF8).ReadToEndAsync();
request.Body.Position = 0;
var ip = request.HttpContext.Connection.RemoteIpAddress?.ToString();
return $"Method: {request.Method}\n" +
$"URL: {request.Scheme}://{request.Host}{request.Path}{request.QueryString}\n" +
$"Headers: \n{headers}" +
$"Body: {body}\n" +
$"IP: {ip}";
}
/// <summary>
/// 格式化回應
/// </summary>
private async Task<string> FormatResponse(HttpResponse response)
{
response.Body.Seek(0, SeekOrigin.Begin);
var headers = FormatHeaders(response.Headers);
var body = await new StreamReader(response.Body, Encoding.UTF8).ReadToEndAsync();
response.Body.Seek(0, SeekOrigin.Begin);
return $"Status code: {response.StatusCode}\n" +
$"Headers: \n{headers}" +
$"Body: {body}";
}
/// <summary>
/// 格式化標頭
/// </summary>
private string FormatHeaders(IHeaderDictionary headers)
{
var formattedHeaders = new StringBuilder();
foreach (var (key, value) in headers)
{
formattedHeaders.AppendLine($"\t{key}: {string.Join(",", value)}");
}
return formattedHeaders.ToString();
}
}
使用中介軟體
var app = builder.Build();
app.UseMiddleware<ApiLoggingMiddleware>();
測試用的範例 API:
[ApiController]
public class UserController : ControllerBase
{
[HttpGet("/api/users")]
public ActionResult GetUsers([FromQuery] int userId)
{
return Ok(
new List<object>
{
new
{
Id = 1,
Name = "User1"
}
}
);
}
}
範例輸出:
info: Request:
Method: GET
URL: https://localhost:7170/api/users?userId=123
Headers:
Accept: */*
Connection: keep-alive
Host: localhost:7170
User-Agent: PostmanRuntime/7.32.3
Accept-Encoding: gzip, deflate, br
Postman-Token: 4cbdfd7e-3a01-42b7-ae12-3e23181628fe
Body:
IP: 127.0.0.1
info: Response:
Status code: 200
Headers:
Content-Type: application/json; charset=utf-8
Body: [{"id":1,"name":"User1"}]
目前是通通寫在 log 中,這樣所有 API 請求回應和一般的 log 都混在一起,要除錯有夠麻煩,該怎麼辦?
那可以參考看看下方的連結,是昨天的文章,介紹使用 Serilog 套件輸出 log 時該如何將內容拆分為多個檔案。
延伸閱讀:C# ASP.NET Core 6 依照功能拆分 Serilog 套件輸出的 log 檔案
留言
張貼留言
如果有任何問題、建議、想說的話或文章題目推薦,都歡迎留言或來信: a@ruyut.com