using System.Diagnostics;
using CoreAgent.Infrastructure.Options;
using System.Text;
using Microsoft.AspNetCore.Http;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
namespace CoreAgent.Infrastructure.Extensions.Logging;
///
/// 响应日志中间件
///
public class ResponseLoggingMiddleware
{
private readonly RequestDelegate _next;
private readonly ILogger _logger;
private readonly RequestLoggingOptions _options;
public ResponseLoggingMiddleware(
RequestDelegate next,
ILogger logger,
IOptions options)
{
_next = next;
_logger = logger;
_options = options.Value;
}
public async Task InvokeAsync(HttpContext context)
{
if (ShouldSkipLogging(context))
{
await _next(context);
return;
}
// 保存原始响应流
var originalBodyStream = context.Response.Body;
try
{
// 创建一个新的内存流来捕获响应
using var responseBody = new MemoryStream();
context.Response.Body = responseBody;
// 继续处理请求
await _next(context);
// 读取响应内容
responseBody.Seek(0, SeekOrigin.Begin);
var response = await ReadResponseBodyAsync(responseBody);
// 记录响应
_logger.LogInformation(
"Response for {Method} {Path}: {StatusCode} - {Response}",
context.Request.Method,
context.Request.Path,
context.Response.StatusCode,
response);
// 将响应写回原始流
responseBody.Seek(0, SeekOrigin.Begin);
await responseBody.CopyToAsync(originalBodyStream);
}
finally
{
// 确保响应体被恢复
context.Response.Body = originalBodyStream;
}
}
private bool ShouldSkipLogging(HttpContext context)
{
if (!_options.LogResponseBody)
{
return true;
}
if (_options.ExcludePaths.Any(p => context.Request.Path.StartsWithSegments(p)))
{
return true;
}
if (_options.ExcludeMethods.Contains(context.Request.Method))
{
return true;
}
return false;
}
private async Task ReadResponseBodyAsync(Stream responseBody)
{
using var reader = new StreamReader(
responseBody,
encoding: Encoding.UTF8,
detectEncodingFromByteOrderMarks: false,
bufferSize: 1024 * 45,
leaveOpen: true);
var body = await reader.ReadToEndAsync();
return body.Length > _options.MaxResponseBodySize
? body.Substring(0, _options.MaxResponseBodySize) + "..."
: body;
}
}