using System; using System.IO; using System.Text.Json; using System.Threading.Tasks; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Haoliang.Core.Services; namespace Haoliang.Api.Middleware { public class LoggingMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; private readonly ILoggingService _loggingService; public LoggingMiddleware(RequestDelegate next, ILogger logger, ILoggingService loggingService) { _next = next; _logger = logger; _loggingService = loggingService; } public async Task Invoke(HttpContext context) { var originalBodyStream = context.Response.Body; try { // Log request await LogRequestAsync(context); // Capture response using (var responseBody = new MemoryStream()) { context.Response.Body = responseBody; await _next(context); // Log response await LogResponseAsync(context, responseBody); // Copy the response body to the original stream responseBody.Seek(0, SeekOrigin.Begin); await responseBody.CopyToAsync(originalBodyStream); } } catch (Exception ex) { await _loggingService.LogErrorAsync($"Unhandled exception in logging middleware: {ex.Message}", ex); throw; } } private async Task LogRequestAsync(HttpContext context) { try { var request = context.Request; // Don't log request body for sensitive endpoints like login var shouldLogBody = !request.Path.ToString().Contains("/login") && !request.Path.ToString().Contains("/auth"); var requestBody = shouldLogBody ? await GetRequestBodyAsync(request) : "[REDACTED]"; var logData = new { Method = request.Method, Path = request.Path, QueryString = request.QueryString.ToString(), Headers = GetSanitizedHeaders(request.Headers), Body = requestBody, UserAgent = request.Headers["User-Agent"].ToString(), RemoteIpAddress = context.Connection.RemoteIpAddress?.ToString(), Timestamp = DateTime.Now }; await _loggingService.LogInfoAsync($"Incoming request: {JsonSerializer.Serialize(logData)}"); _logger.LogInformation("Incoming request: {Request}", JsonSerializer.Serialize(logData)); } catch (Exception ex) { await _loggingService.LogErrorAsync($"Error logging request: {ex.Message}", ex); } } private async Task LogResponseAsync(HttpContext context, MemoryStream responseBody) { try { var response = context.Response; var responseBodyContent = await GetResponseBodyAsync(responseBody); var logData = new { StatusCode = response.StatusCode, Headers = GetSanitizedHeaders(response.Headers), Body = responseBodyContent, Timestamp = DateTime.Now }; await _loggingService.LogInfoAsync($"Outgoing response: {JsonSerializer.Serialize(logData)}"); _logger.LogInformation("Outgoing response: {Response}", JsonSerializer.Serialize(logData)); } catch (Exception ex) { await _loggingService.LogErrorAsync($"Error logging response: {ex.Message}", ex); } } private async Task GetRequestBodyAsync(HttpRequest request) { request.EnableBuffering(); using (var reader = new StreamReader(request.Body, Encoding.UTF8, true, 1024, true)) { var body = await reader.ReadToEndAsync(); request.Body.Seek(0, SeekOrigin.Begin); return body; } } private async Task GetResponseBodyAsync(MemoryStream responseBody) { responseBody.Seek(0, SeekOrigin.Begin); using (var reader = new StreamReader(responseBody, Encoding.UTF8)) { var body = await reader.ReadToEndAsync(); return body; } } private object GetSanitizedHeaders(IHeaderDictionary headers) { var sensitiveHeaders = new[] { "authorization", "cookie", "set-cookie", "password", "token" }; var sanitizedHeaders = new Dictionary(); foreach (var header in headers) { if (sensitiveHeaders.Contains(header.Key.ToLower())) { sanitizedHeaders[header.Key] = "[REDACTED]"; } else { sanitizedHeaders[header.Key] = header.Value.ToString(); } } return sanitizedHeaders; } } public static class LoggingMiddlewareExtensions { public static IApplicationBuilder UseLoggingMiddleware(this IApplicationBuilder builder) { return builder.UseMiddleware(); } } }