前言
在上篇文章《.Net5 框架搭建(三):基于ExceptionFilter+NLog封装异常日志收集》中我们提到了过滤器,也对异常信息做了日志记录,本文接着对五大过滤器中的操作过滤器Action Filter进一步实战功能的延续。
问:那么什么业务场景适合用到操作过滤器呢?
答:日志统计、权限过滤
ActionFilter/操作过滤器
官网截图
大致意思,提供两种接口IActionFilter 或 IAsyncActionFilter 接口。
1、IActionFilter
这个接口提供两个方法
OnActionExecuted(ActionExecutedContext) 在操作执行之后、操作结果之前调用。
OnActionExecuting(ActionExecutingContext) 在模型绑定完成后,在执行操作之前调用。
2、IAsyncActionFilter
只提供一种方法
OnActionExecutionAsync(ActionExecutingContext, ActionExecutionDelegate)
模型绑定完成后,在操作之前以异步方式调用。
创建一个自定义操作过滤器
这里我是继承了IAsyncActionFilter,就是为了省麻烦
LogActionFilter.cs
/// <summary>
/// 操作日志过滤器
/// </summary>
public class LogActionFilter : IAsyncActionFilter
{
private readonly ILogHandler _logHandler;
public LogActionFilter(ILogHandler logHandler)
{
_logHandler = logHandler;
}
public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (context.ActionDescriptor.EndpointMetadata.Any(m => m.GetType() == typeof(NoLogAttribute)))
{
return next();
}
return _logHandler.LogAsync(context, next);
}
}
新增一个自定义属性类,用于区别一些不想做操作日志记录的方法
NoLogAttribute.cs
/// <summary>
/// 不记录操作日志的属性
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class NoLogAttribute : Attribute
{
}
LogHandler.cs
/// <summary>
/// 操作日志处理
/// </summary>
public class LogHandler : ILogHandler
{
private readonly ILogger _logger;
private readonly ISystemOperateLogBLL _systemOperateLogBLL;
public LogHandler(ILogger<LogHandler> logger, ISystemOperateLogBLL systemOperateLogBLL)
{
_logger = logger;
_systemOperateLogBLL = systemOperateLogBLL;
}
public async Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var sw = new Stopwatch();
sw.Start();
var actionExecutedContext = await next();
sw.Stop();
//操作参数
var args = JsonConvert.SerializeObject(context.ActionArguments);
//操作结果
//var result1 = JsonConvert.SerializeObject(actionResult?.Value);
try
{
var model = new SystemOperateLog
{
Params = args,
ApiMethod = context.HttpContext.Request.Method.ToLower(),
ApiPath = context.ActionDescriptor.AttributeRouteInfo.Template.ToLower(),
ElapsedMilliseconds = sw.ElapsedMilliseconds.ObjToInt()
};
ObjectResult result = actionExecutedContext.Result as ObjectResult;
if (result != null)
{
model.Result = JsonConvert.SerializeObject(result.Value);
model.LogStatus = 1;
}
string ua = context.HttpContext.Request.Headers["User-Agent"];
//记得添加UAParser
var client = UAParser.Parser.GetDefault().Parse(ua);
var device = client.Device.Family;
device = device.ToLower() == "other" ? "" : device;
model.Browser = client.UA.Family;
model.Os = client.OS.Family;
model.Device = device;
model.BrowserInfo = ua;
model.IP = IPHelper.GetIP(context?.HttpContext?.Request);
await _systemOperateLogBLL.AddLog(model);
}
catch (Exception ex)
{
_logger.LogError("操作日志插入异常:{@ex}", ex);
}
}
}
ILogHandler.cs
/// <summary>
/// 操作日志处理接口
/// </summary>
public interface ILogHandler
{
/// <summary>
/// 写操作日志
/// </summary>
/// <param name="context"></param>
/// <param name="next"></param>
/// <returns></returns>
Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next);
}
IPHelper.cs
public class IPHelper
{
/// <summary>
/// 是否为ip
/// </summary>
/// <param name="ip"></param>
/// <returns></returns>
public static bool IsIP(string ip)
{
return Regex.IsMatch(ip, @"^((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){3}(2[0-4]\d|25[0-5]|[01]?\d\d?)$");
}
/// <summary>
/// 获得IP地址
/// </summary>
/// <param name="request"></param>
/// <returns></returns>
public static string GetIP(HttpRequest request)
{
if (request == null)
{
return "";
}
string ip = request.Headers["X-Real-IP"].FirstOrDefault();
if (!ip.IsNotEmptyOrNull())
{
ip = request.Headers["X-Forwarded-For"].FirstOrDefault();
}
if (!ip.IsNotEmptyOrNull())
{
ip = request.HttpContext?.Connection?.RemoteIpAddress?.ToString();
}
if (!ip.IsNotEmptyOrNull() || !IsIP(ip.Split(":")[0]))
{
ip = "127.0.0.1";
}
return ip;
}
/// <summary>
/// 获得MAC地址
/// </summary>
/// <returns></returns>
public static string GetMACIp()
{
//本地计算机网络连接信息
//IPGlobalProperties computerProperties = IPGlobalProperties.GetIPGlobalProperties();
//获取本机电脑名
//var HostName = computerProperties.HostName;
//获取域名
//var DomainName = computerProperties.DomainName;
//获取本机所有网络连接
NetworkInterface[] nics = NetworkInterface.GetAllNetworkInterfaces();
if (nics == null || nics.Length < 1)
{
return "";
}
var MACIp = "";
foreach (NetworkInterface adapter in nics)
{
var adapterName = adapter.Name;
var adapterDescription = adapter.Description;
var NetworkInterfaceType = adapter.NetworkInterfaceType;
if (adapterName == "本地连接" || adapterName == "WLAN")
{
PhysicalAddress address = adapter.GetPhysicalAddress();
byte[] bytes = address.GetAddressBytes();
for (int i = 0; i < bytes.Length; i++)
{
MACIp += bytes[i].ToString("X2");
if (i != bytes.Length - 1)
{
MACIp += "-";
}
}
}
}
return MACIp;
}
}
全局注册
services.AddControllers(options =>
{
//添加全局异常过滤器
options.Filters.Add<GlobalExceptionsFilter>();
//日志过滤器
options.Filters.Add<LogActionFilter>();
});
测试
[HttpGet("getUser")]
public async Task<dynamic> Get(long id)
{
//_logger.LogError("LogError3333");
var obj = await _systemUserBLL.GetUser(id);
return obj;
}
[NoLog]
[HttpPost("updateUser")]
public dynamic UpdateUser(SystemUser user)
{
var obj = _systemUserBLL.Update(user);
return obj;
}
期望效果:记录getUser、不记录updateUser
运行截图
完整代码已上传码云:https://gitee.com/shao-jiayong/cuo-ding