namespace MiaoYu.Shared.Admin.Middlewares; /// /// 统计耗时 并记录接口访问日志 中间件 /// public class TakeUpTimeMiddleware : IMiddleware { private readonly Stopwatch _stopwatch; private readonly ILogger _logger; private readonly HttpContext? _httpContext; private readonly IAccountService _accountService; private readonly IProducer _producer; private readonly IConfiguration _configuration; /// /// 统计耗时 并记录日志中 中间件 /// /// /// /// /// /// public TakeUpTimeMiddleware(ILogger logger, IHttpContextAccessor iHttpContextAccessor, IAccountService accountService, IProducer producer, IConfiguration configuration) { _stopwatch ??= new Stopwatch(); _logger = logger; _httpContext = iHttpContextAccessor.HttpContext; _accountService = accountService; _producer = producer; _configuration = configuration; } /// /// /// /// /// /// public async Task InvokeAsync(HttpContext context, RequestDelegate next) { var apiList = _configuration.GetSection("ApiWhiteList")?.Get(); var path = context.Request.Path; if (apiList != null && apiList.Any(w => path.ToString().ToLower().Contains(w.ToLower()))) { var sw = Stopwatch.StartNew(); context.Response.OnStarting(() => { sw.Stop(); context.Response.Headers.TryAdd("X-Request-TotalMilliseconds", $"{sw.Elapsed.TotalMilliseconds} ms"); return Task.CompletedTask; }); await next.Invoke(context); return; } //获取body var bodyString = await ReadBodyAsync(); //记录 api 执行耗时 _stopwatch.Restart(); context.Response.OnStarting(() => { context.Response.Headers.TryAdd("X-Request-TotalMilliseconds", $"{_stopwatch.Elapsed.TotalMilliseconds} ms"); return Task.CompletedTask; }); await next.Invoke(context); _stopwatch.Stop(); if (IsApi(context)) { var remoteIpAddress = context.Connection.RemoteIpAddress; var log = $"{remoteIpAddress} 请求:{path} 耗时:{_stopwatch.ElapsedMilliseconds} 毫秒!"; _logger.LogInformation(log); var endpoint = context.GetEndpoint(); await WriteInLogAsync(_stopwatch.ElapsedMilliseconds, bodyString, endpoint); } } /// /// 判断请求类型 是否 是 api /// /// /// private bool IsApi(HttpContext context) { var contentTypes = new[] { "application/json", "text/html" }; return string.IsNullOrWhiteSpace(context.Response.ContentType) || contentTypes.Any(w => context.Response.ContentType.StartsWith(w)); } /// /// 读取 body 信息 /// /// private async Task ReadBodyAsync() { try { if (_httpContext.Request.ContentType != null && _httpContext.Request.ContentType.Contains("multipart/form-data")) return default; //获取body _httpContext.Request.EnableBuffering();//可以实现多次读取Body var sr = new StreamReader(_httpContext.Request.Body); var bodyString = await sr.ReadToEndAsync(); _httpContext.Request.Body.Seek(0, SeekOrigin.Begin); return bodyString; } catch (Exception) { return default; } } /// /// 写入操作日志 /// /// public async Task WriteInLogAsync(long time, string bodyString, Endpoint endpoint) { var queryString = _httpContext.Request.QueryString.ToString(); var apiUrl = _httpContext.Request.Path; //获取请求ip var ip = _httpContext.Request.Headers["X-Forwarded-For"].FirstOrDefault(); if (string.IsNullOrEmpty(ip)) { ip = _httpContext.Connection.RemoteIpAddress.MapToIPv4().ToString(); } // var clientInfo = _httpContext.GetBrowserClientInfo(); var browser = clientInfo?.UA.Family + clientInfo?.UA.Major; var os = clientInfo?.OS.Family + clientInfo?.OS.Major; //本机不记录 // if (_IP == "::1") return; var formString = string.Empty; //form try { if (_httpContext.Request.HasFormContentType) { //读取 表单 信息 var form = await _httpContext.Request.ReadFormAsync(); if (form != null) { var _Dictionary = new Dictionary(); foreach (var key in form.Keys) { _Dictionary[key] = form[key]; } formString = JsonConvert.SerializeObject(_Dictionary); } } } catch (Exception) { } var userInfo = _accountService.GetAccountContext(); var sysOperationLog = new SysOperationLog { Api = apiUrl, Ip = ip, Form = formString, QueryString = queryString, FormBody = bodyString, UserId = userInfo?.Id, TakeUpTime = time, Browser = browser, OS = os, }; // if (endpoint != null) { var controllerDescriptorAttribute = endpoint.Metadata.GetMetadata(); var actionDescriptorAttribute = endpoint.Metadata.GetMetadata(); sysOperationLog.ControllerDisplayName = controllerDescriptorAttribute?.DisplayName; sysOperationLog.ActionDisplayName = actionDescriptorAttribute?.DisplayName; } // 发布消息 await _producer.PublishAsync("WriteInLog", sysOperationLog); } }