216 lines
6.4 KiB
C#
216 lines
6.4 KiB
C#
namespace MiaoYu.Shared.Admin.Middlewares;
|
|
|
|
/// <summary>
|
|
/// 统计耗时 并记录接口访问日志 中间件
|
|
/// </summary>
|
|
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;
|
|
|
|
/// <summary>
|
|
/// 统计耗时 并记录日志中 中间件
|
|
/// </summary>
|
|
/// <param name="logger"></param>
|
|
/// <param name="iHttpContextAccessor"></param>
|
|
/// <param name="accountService"></param>
|
|
/// <param name="producer"></param>
|
|
/// <param name="configuration"></param>
|
|
public TakeUpTimeMiddleware(ILogger<TakeUpTimeMiddleware> logger,
|
|
IHttpContextAccessor iHttpContextAccessor,
|
|
IAccountService accountService,
|
|
IProducer producer,
|
|
IConfiguration configuration)
|
|
{
|
|
_stopwatch ??= new Stopwatch();
|
|
_logger = logger;
|
|
_httpContext = iHttpContextAccessor.HttpContext;
|
|
_accountService = accountService;
|
|
_producer = producer;
|
|
_configuration = configuration;
|
|
}
|
|
|
|
/// <summary>
|
|
///
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
/// <param name="next"></param>
|
|
/// <returns></returns>
|
|
public async Task InvokeAsync(HttpContext context, RequestDelegate next)
|
|
{
|
|
var apiList = _configuration.GetSection("ApiWhiteList")?.Get<string[]>();
|
|
|
|
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);
|
|
}
|
|
}
|
|
|
|
/// <summary>
|
|
/// 判断请求类型 是否 是 api
|
|
/// </summary>
|
|
/// <param name="context"></param>
|
|
/// <returns></returns>
|
|
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));
|
|
}
|
|
|
|
/// <summary>
|
|
/// 读取 body 信息
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
private async Task<string?> 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;
|
|
}
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// 写入操作日志
|
|
/// </summary>
|
|
/// <returns></returns>
|
|
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<string, object>();
|
|
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<ControllerDescriptorAttribute>();
|
|
var actionDescriptorAttribute = endpoint.Metadata.GetMetadata<ActionDescriptorAttribute>();
|
|
sysOperationLog.ControllerDisplayName = controllerDescriptorAttribute?.DisplayName;
|
|
sysOperationLog.ActionDisplayName = actionDescriptorAttribute?.DisplayName;
|
|
}
|
|
|
|
// 发布消息
|
|
await _producer.PublishAsync("WriteInLog", sysOperationLog);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
} |