一、前言
目的和原則
1、程序任何地方都不能catch掉異常,如果要catch也請重新throw異常或是將異常記錄到日志里。避免異常被“吃掉“,導致無法排查程序的bug。
2、webapi接口的”請求成功“和”請求失敗“以一定的標准規范提供給外部
3、如果為已知異常(即我們代碼里寫的throw異常)可簡單的記錄到日志,但如果是未知異常(我們不知道是哪里拋出的異常,往往也是程序的bug)則記錄到特殊的日志文件里,如上篇的log/error目錄下。
二、新建公共方法類庫
1.新建一個類庫用來存儲公共方法以供項目或者其他庫調用。
2.安裝Newtonsoft
3.新建Utils類,寫一個返回Json的類
public class Utils { public static HttpResponseMessage toJson(dynamic code,object result) { var response = Newtonsoft.Json.JsonConvert.SerializeObject(result); HttpResponseMessage res = new HttpResponseMessage(code); res.Content = new StringContent(response, Encoding.UTF8, "application/json"); return res; } }
4.系統的HttpStatusCode枚舉可能不能滿足我們的需求,所以新建枚舉類HttpCode,定義我們的返回碼:
public enum HttpCode { /// <summary> /// 成功 /// </summary> SUCCESS = 200, /// <summary> /// 失敗 /// </summary> ERROR = 500, /// <summary> /// 參數錯誤 /// </summary> ERROR_PARAM = 600, /// <summary> /// 參數為空 /// </summary> NULL_PARAM = 601, /// <summary> /// 數據庫異常 /// </summary> DB_ERROR = 600, /// <summary> /// 數據操作成功 /// </summary> OK = 1, /// <summary> /// 數據操作成功 /// </summary> FAILED = 0, }
4.引用公共類庫
二、定義異常
1.新建Exceptions文件夾,新建KnownException類,繼承Exception,這個類主要是拋出已知的異常信息
public class KnownException : Exception { public HttpCode code; public string msg; public KnownException(HttpCode code, string msg) { this.code = code; this.msg = msg; } }
2.項目Models文件夾下創建一個Result類,用來定義消息返回格式,對於 null
添加注解 [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
數據可以選擇忽略,不返回給客戶端
public class Result { /// <summary> /// 碼值 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public HttpCode code; /// <summary> /// 信息 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public string msg; /// <summary> /// 具體的數據 /// </summary> [JsonProperty(NullValueHandling = NullValueHandling.Ignore)] public object data; public Result() { } public Result(HttpCode code, string msg, object data) { this.code = code; this.msg = msg; this.data = data; } }
3.Exceptions文件夾下新建WebApiExceptionFilterAttribute.cs,繼承ExceptionFilterAttribute,重寫OnException方法
/// <summary> /// 異常處理 /// </summary> public class WebApiExceptionFilterAttribute : ExceptionFilterAttribute { public override void OnException(HttpActionExecutedContext actionExecutedContext) { var exception = actionExecutedContext.Exception;//獲取產生的異常對象 var exceptionMessage = exception.Message; var logMessage = $@"controller.action={actionExecutedContext.ActionContext.ControllerContext.ControllerDescriptor.ControllerName}.{actionExecutedContext.ActionContext.ActionDescriptor.ActionName}:exception=" + exception.Message;//異常內容 ILog log = LogManager.GetLogger(actionExecutedContext.ActionContext.ControllerContext.Controller.GetType()); Result result = new Result(); if (exception is KnownException)//如果是已知異常 { log.Debug(logMessage); var ex = (KnownException)actionExecutedContext.Exception; result.code = ex.code; result.msg = ex.msg; } else//如果是未知異常 { log.Error(logMessage, exception); result.code = HttpCode.ERROR; result.msg = "內部錯誤"; result.data = exceptionMessage; } actionExecutedContext.ActionContext.Response = Utils.toJson(HttpStatusCode.BadRequest, result); } }
4.將異常過濾器加到webapiconfig.cs里
config.Filters.Add(new WebApiExceptionFilterAttribute());
5.控制器新建一個請求,分別模擬拋出已知異常和未知異常:
[Route("know")] [HttpGet] public IHttpActionResult Know() { throw new KnownException(HttpCode.DB_ERROR,"數據庫異常了"); } [HttpGet] [Route("unknow")] public IHttpActionResult UnKnow() { throw new System.IO.IOException(); }
6.測試結果:
7.異常結果也輸出到了日志:
二、消息異常處理
當我們輸入一個不存在的請求的時候,會提示控制器不存在的信息,如果我們想自定義消息返回格式,可以通過DelegatingHandler去重寫異常信息。
1.Expections文件夾新建CustomErrorMessageDelegatingHandler.cs繼承DelegatingHandler
public class CustomErrorMessageDelegatingHandler : DelegatingHandler { protected override Task<HttpResponseMessage> SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { return base.SendAsync(request, cancellationToken).ContinueWith(ResponseMessage); } public HttpResponseMessage ResponseMessage(Task<HttpResponseMessage> request) { HttpResponseMessage response = request.Result; HttpError error = null; if (response.TryGetContentValue<HttpError>(out error)) { //添加自定義錯誤處理 //error.Message = "Your Customized Error Message"; } if (error != null) { ////獲取拋出自定義異常,有攔截器統一解析 return Utils.toJson(response.StatusCode, new { code = response.StatusCode, message = error.Message }); } else { return response; } } }
2.webapiconfig.cs注冊
onfig.MessageHandlers.Add(new CustomErrorMessageDelegatingHandler());
3.輸入一個不存在的請求: