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; } }