又到了疯狂星期五了
今天我们利用过滤器实现一个全局异常和一个全局日志的功能。
废话不说,直接上干货:
1、新建一个Filters文件夹,添加文件GlobalExceptionFilter.cs,添加如下代码:
namespace NET6.Api.Filters;
public class GlobalExceptionFilter : IExceptionFilter
{
readonly IWebHostEnvironment hostEnvironment;
public GlobalExceptionFilter(IWebHostEnvironment _hostEnvironment)
{
hostEnvironment = _hostEnvironment;
}
public void OnException(ExceptionContext context)
{
if (!context.ExceptionHandled)
{
var result = new JsonView
{
Code = (int)HttpStatusCode.InternalServerError,
Msg = "服务器发生未处理的异常"
};
if (hostEnvironment.IsDevelopment())
{
result.Msg += ":" + context.Exception.Message;
result.Data = context.Exception.StackTrace;
}
context.Result = new ContentResult
{
StatusCode = (int)HttpStatusCode.InternalServerError,
ContentType = "application/json;charset=utf-8",
Content = result.ToJson()
};
Log.Error($"服务器内部错误:{result.ToJson()}");
context.ExceptionHandled = true;
}
}
}
2、同样新建LogActionFilter.cs,添加如下代码:
namespace NET6.Api.Filters;
/// <summary>
/// 日志过滤器
/// </summary>
public class LogActionFilter : IAsyncActionFilter
{
readonly OperationLogRepository _logRep;
public LogActionFilter(OperationLogRepository logRep)
{
_logRep = logRep;
}
public Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
if (context.ActionDescriptor.EndpointMetadata.Any(a => a.GetType() == typeof(NoLogAttribute)))
{
return next();
}
return LogAsync(context, next);
}
private async Task LogAsync(ActionExecutingContext context, ActionExecutionDelegate next)
{
var sw = new Stopwatch();
sw.Start();
var actionResult = (await next()).Result;
sw.Stop();
var args = context.ActionArguments.ToJson();
var result = actionResult?.ToJson();
var request = BuilderExtensions.ServiceProvider.GetRequiredService<IHttpContextAccessor>().HttpContext?.Request;
var ua = request?.Headers["User-Agent"];
var client = UAParser.Parser.GetDefault().Parse(ua);
var controller = ((ControllerActionDescriptor)context.ActionDescriptor).ControllerName.ToLower();
var action = ((ControllerActionDescriptor)context.ActionDescriptor).ActionName.ToLower();
var log = new OperationLog
{
ApiMethod = context.HttpContext.Request.Method.ToLower(),
ApiPath = $"/{controller}/{action}",
ElapsedMilliseconds = sw.ElapsedMilliseconds,
Params = args,
Result = result,
Browser = client.UA.ToString(),
Os = client.OS.ToString(),
Device = client.Device.ToString(),
BrowserInfo = ua,
IP = CommonFun.GetIP(request)
};
//自动分表插入
await _logRep.AddSplitTableAsync(log);
}
}
此处我们使用Sqlsugar的自动分表功能来实现分表插入,当然根据你的日志量大小用单表也可以实现。
3、我们新建一个Attribute用来标记需要记录日志的操作或者不需要记录日志的操作,此处我们新建一个不记录的NoLogAttribute:
namespace NET6.Api.Attributes;
/// <summary>
/// 不记录操作日志
/// </summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true)]
public class NoLogAttribute : Attribute
{
}
4、一切准备就绪,我们那将这两个过滤器添加到Program中:
// Add services to the container.
builder.Services.AddControllersWithViews(options =>
{
options.Filters.Add<LogActionFilter>();
options.Filters.Add<GlobalExceptionFilter>();
})
5、我们将NoLogAttribute随意放置到一个Action上,然后启动项目,访问接口会发现,除了我们放置NoLogAttribute的那个方法,其他请求都已经成功生成日志:
6、我们在Auth的Loin方法中抛出一个异常:
请求接口发现异常已经按照Json格式成功抛出