在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);
}
