在webapi 項目中,經常需要記錄異常信息和接口的請求詳情,同時記錄調用的接口異常的參數等數據以便后續追查,但是又不想在項目到處寫try catch,此時可以通過全局過濾器進行記錄,
代碼如下
全局異常過濾器
WebApiExceptionFilterAttribute
namespace NetCore3WebApiTemplate.Filters { /// <summary> /// api請求異常日志類 /// </summary> [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = false)] public class WebApiExceptionFilterAttribute : ExceptionFilterAttribute, IActionFilter { /// <summary> /// 控制器中的操作執行之前調用此方法 /// </summary> /// <param name="context"></param> public void OnActionExecuting(ActionExecutingContext context) {
//將請求的參數帶上,后續使用 context.HttpContext.Items.Add("params", context.ActionArguments);
//請求開始實際帶上 context.HttpContext.Items.Add("executeStartTime", DateTime.Now); } /// <summary> /// 控制器中的操作執行之后調用此方法 /// </summary> /// <param name="context"></param> public void OnActionExecuted(ActionExecutedContext context) { // do nothing } /// <summary> /// 控制器中的操作異常調用此方法 /// </summary> /// <param name="actionExecutedContext"></param> /// <returns></returns> public override Task OnExceptionAsync(ExceptionContext actionExecutedContext) { if (actionExecutedContext.Exception != null) { ///取消操作導致的異常忽略 if (actionExecutedContext.Exception is OperationCanceledException) { } else { WriteErrorAsync(actionExecutedContext); } } HttpResponseResultModel<string> result = new HttpResponseResultModel<string>(); result.HttpStatusCode = (HttpStatusCode)actionExecutedContext.HttpContext.Response.StatusCode; result.IsSuccess = false; result.ErrorMessage = "出現異常,請稍后重試"; result.ExceptionMessage = actionExecutedContext.Exception.ToString(); actionExecutedContext.Result = new ObjectResult(result); return base.OnExceptionAsync(actionExecutedContext); } /// <summary> /// 寫異常日志 /// </summary> /// <param name="exceptionContext"></param> /// <returns></returns> private async Task WriteErrorAsync(ExceptionContext exceptionContext) { WebApiExceptionLogModel logModel = new WebApiExceptionLogModel(); //獲取Action 參數 var items = exceptionContext.HttpContext.Items; logModel.ExecuteStartTime = DateTime.Parse(items["executeStartTime"].ToString()); logModel.ExecuteEndTime = DateTime.Now; IDictionary<string, object> actionArguments = null; if (items.ContainsKey("params")) { actionArguments = (IDictionary<string, object>)items["params"]; } logModel.ActionParams = new Dictionary<string, object>(actionArguments); logModel.HttpRequestHeaders = exceptionContext.HttpContext.Request.Headers.ToString(); logModel.HttpRequestPath = exceptionContext.HttpContext.Request.Path; logModel.HttpMethod = exceptionContext.HttpContext.Request.Method; logModel.ActionName = ((ControllerActionDescriptor)exceptionContext.ActionDescriptor).ActionName; logModel.ControllerName = ((ControllerActionDescriptor)exceptionContext.ActionDescriptor).ControllerName; logModel.TotalSeconds = (logModel.ExecuteEndTime - logModel.ExecuteStartTime).TotalSeconds; logModel.ExceptionMessage = exceptionContext.Exception.ToString(); logModel.IP = CommonHttpContext.Current.Connection.RemoteIpAddress.ToString(); logModel.StatusCode = exceptionContext.HttpContext.Response.StatusCode; } } }
全局日志過濾器 WebApiTrackerAttribute 代碼如下
namespace NetCore3WebApiTemplate.Filters { /// <summary> /// api 日志跟蹤類 /// </summary> public class WebApiTrackerAttribute : ActionFilterAttribute { /// <summary> /// 控制器方法執行之前執行此方法 /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> public override Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) { context.HttpContext.Items.Add("executeStartTime", DateTime.Now); WriteLogAsync(context); return base.OnActionExecutionAsync(context, next); } /// <summary> /// 控制器操作結果執行之前調用此方法 /// </summary> /// <param name="context"></param> /// <param name="next"></param> /// <returns></returns> public override Task OnResultExecutionAsync(ResultExecutingContext context, ResultExecutionDelegate next) { return base.OnResultExecutionAsync(context, next); } /// <summary> /// 控制器操作結果執行之后調用此方法 /// </summary> /// <param name="context"></param> public override void OnActionExecuted(ActionExecutedContext context) { base.OnActionExecuted(context); } /// <summary> /// 寫日志 /// </summary> /// <param name="actionContext"></param> /// <returns></returns> private async Task WriteLogAsync(ActionExecutingContext actionContext) { var items = actionContext.HttpContext.Items; DateTime executeStartTime = DateTime.Parse(items["executeStartTime"].ToString()); WebApiLogModel logModel = new WebApiLogModel(); logModel.ExecuteStartTime = executeStartTime; logModel.ExecuteEndTime = DateTime.Now; //獲取Action 參數 logModel.ActionParams = new Dictionary<string, object>(actionContext.ActionArguments); logModel.HttpRequestHeaders = actionContext.HttpContext.Request.Headers.ToString(); logModel.HttpRequestPath = actionContext.HttpContext.Request.Path; logModel.HttpMethod = actionContext.HttpContext.Request.Method; logModel.ActionName = ((ControllerActionDescriptor)actionContext.ActionDescriptor).ActionName; logModel.ControllerName = ((ControllerActionDescriptor)actionContext.ActionDescriptor).ControllerName; logModel.TotalSeconds = (logModel.ExecuteEndTime - logModel.ExecuteStartTime).TotalSeconds; logModel.IP = CommonHttpContext.Current.Connection.RemoteIpAddress.ToString(); } } }
using System.Net; namespace NetCore3WebApiTemplate.Core { /// <summary> /// http請求結果類 /// </summary> /// <typeparam name="T"></typeparam> public class HttpResponseResultModel<T> { /// <summary> /// http碼 /// </summary> public HttpStatusCode HttpStatusCode { get; set; } /// <summary> /// 是否成功 /// </summary> public bool IsSuccess { get; set; } /// <summary> /// 返回結果 /// </summary> public T BackResult { get; set; } /// <summary> /// 錯誤信息 /// </summary> public string ErrorMessage { get; set; } /// <summary> /// 異常信息 /// </summary> public string ExceptionMessage { get; set; } } }
namespace NetCore3WebApiTemplate.Utility { /// <summary> /// webapi異常日志類 /// </summary> public class WebApiExceptionLogModel: WebApiLogModel { /// <summary> /// 異常信息 /// </summary> public string ExceptionMessage { get; set; } /// <summary> /// 狀態碼 /// </summary> public int StatusCode { get; set; } } }
using System; using System.Collections.Generic; using System.Text; namespace NetCore3WebApiTemplate.Utility { /// <summary> /// webapi 日志類 /// </summary> public class WebApiLogModel { /// <summary> /// 控制器名稱 /// </summary> public string ControllerName { get; set; } /// <summary> /// 方法名稱 /// </summary> public string ActionName { get; set; } /// <summary> /// 執行開始時間 /// </summary> public DateTime ExecuteStartTime { get; set; } /// <summary> /// 執行結束時間 /// </summary> public DateTime ExecuteEndTime { get; set; } /// <summary> /// 總耗時(秒) /// </summary> public double TotalSeconds { get; set; } /// <summary> /// 請求的Action 參數 /// </summary> public Dictionary<string, object> ActionParams { get; set; } /// <summary> /// Http請求頭 /// </summary> public string HttpRequestHeaders { get; set; } /// <summary> /// 請求的路徑 /// </summary> public string HttpRequestPath { get; set; } /// <summary> /// 請求方法類型(POST,GET等) /// </summary> public string HttpMethod { get; set; } /// <summary> /// 請求的IP地址 /// </summary> public string IP { get; set; } } }
using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using System; using System.Collections.Generic; using System.Text; namespace NetCore3WebApiTemplate.Utility { public static class CommonHttpContext { private static IHttpContextAccessor accessor; public static HttpContext Current => accessor.HttpContext; internal static void Configure(IHttpContextAccessor accessor) { CommonHttpContext.accessor = accessor; } } public static class StaticHttpContextExtensions { public static IApplicationBuilder UseStaticHttpContext(this IApplicationBuilder app) { var httpContextAccessor = app.ApplicationServices.GetRequiredService<IHttpContextAccessor>(); CommonHttpContext.Configure(httpContextAccessor); return app; } } public static class CommonServiceProvider { public static IServiceProvider ServiceProvider { get; set; } } }
在startup的ConfigureServices中設置或禁用WebApiExceptionFilterAttribute或WebApiTrackerAttribute
/// <summary> /// This method gets called by the runtime. Use this method to add services to the container. /// </summary> /// <param name="services"></param> public void ConfigureServices(IServiceCollection services) { services.AddControllers(ops => { ops.Filters.Add(new WebApiExceptionFilterAttribute()); ops.Filters.Add(new WebApiTrackerAttribute()); }).SetCompatibilityVersion(CompatibilityVersion.Version_3_0); }