概述:
ASP.NET Web API 的好用使用过的都知道,没有复杂的配置文件,一个简单的ApiController加上需要的Action就能工作。但是项目,总有异常发生,本节就来谈谈API的异常的统一处理和写统一写log逻辑的解决方案。
问题:
在ASP.NET Web API编写时,如果每个API都写异常处理逻辑,不但加大了开发工作量,且每个开发人员处理异常返回的数据结构也不尽相同,在异常发生情况下,客户端处理异常的逻辑就不再通用,也同时加大了对接接口人员的工作量,好的API错误码和错误信息都是固定格式,并后台应该有相应的异常记录。
异常的统一处理的实现:
1. 首先定义异常处理Attribute,继承System.Web.Http.Filters.ExceptionAttribute, 重写OnException, 代码如下
1 public class ErrorHandleAttribute : System.Web.Http.Filters.ExceptionFilterAttribute 2 { 3 private string _msg = string.Empty; 4 5 public ErrorHandleAttribute() { } 6 7 public ErrorHandleAttribute(string msg) 8 { 9 this._msg = msg; 10 } 11 public override void OnException(System.Web.Http.Filters.HttpActionExecutedContext actionExecutedContext) 12 { 13 base.OnException(actionExecutedContext); 14 // 取得发生异常时的错误讯息 15 //var errorMessage = actionExecutedContext.Exception.Message; 16 // 标记log 17 var logAction = actionExecutedContext.ActionContext.ActionDescriptor.GetCustomAttributes<NoErrorHandlerAttribute>(); 18 if (logAction.Any()) 19 { 20 actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(System.Net.HttpStatusCode.InternalServerError, new ResultData(ResultType.SystemException, actionExecutedContext.Exception.Message)); 21 return; 22 } 23 24 var request = HttpContext.Current.Request; 25 var logDetail = new LogDetail 26 { 27 //获取action名称 28 ActionName = actionExecutedContext.ActionContext.ActionDescriptor.ActionName, 29 //获取Controller 名称 30 ControllerName = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName, 31 Navigator = request.UserAgent, 32 //获取访问的ip 33 IP = request.UserHostAddress, 34 UserHostName = request.UserHostName, 35 UrlReferrer = request.UrlReferrer != null ? request.UrlReferrer.AbsoluteUri : "", 36 Browser = request.Browser.Browser + " - " + request.Browser.Version + " - " + request.Browser.Type, 37 //获取request提交的参数 38 Paramaters = GetRequestValues(actionExecutedContext), 39 //获取response响应的结果 40 //ExecuteResult = GetResponseValues(actionExecutedContext), //这句会报错,异常没有处理结果 41 AttrTitle = this._msg, 42 ErrorMsg = string.Format("错误信息:{0}, 异常跟踪:{1}", actionExecutedContext.Exception.Message, actionExecutedContext.Exception.StackTrace), 43 RequestUri = request.Url.AbsoluteUri 44 }; 45 46 // 写log 47 var logRep = ContainerManager.Resolve<ISysLogRepository>(); 48 var log = new Log() 49 { 50 Action = actionExecutedContext.ActionContext.ActionDescriptor.ControllerDescriptor.ControllerName + "/" + actionExecutedContext.ActionContext.ActionDescriptor.ActionName, 51 CreateDate = DateTime.Now, 52 CreatorLoginName = RISContext.Current.CurrentUserInfo.UserName, 53 IpAddress = request.UserHostAddress, 54 Detail = Utility.JsonSerialize<LogDetail>(logDetail) 55 }; 56 57 logRep.Add(log); 58 actionExecutedContext.Response = actionExecutedContext.Request.CreateResponse(System.Net.HttpStatusCode.InternalServerError, new ResultData(ResultType.SystemException, actionExecutedContext.Exception.Message)); 59 } 60 61 /// <summary> 62 /// 读取request 的提交内容 63 /// </summary> 64 /// <param name="actionExecutedContext"></param> 65 /// <returns></returns> 66 public string GetRequestValues(HttpActionExecutedContext actionExecutedContext) 67 { 68 69 Stream stream = actionExecutedContext.Request.Content.ReadAsStreamAsync().Result; 70 Encoding encoding = Encoding.UTF8; 71 /* 72 这个StreamReader不能关闭,也不能dispose, 关了就傻逼了 73 因为你关掉后,后面的管道 或拦截器就没办法读取了 74 */ 75 var reader = new StreamReader(stream, encoding); 76 string result = reader.ReadToEnd(); 77 /* 78 这里也要注意: stream.Position = 0; 79 当你读取完之后必须把stream的位置设为开始 80 因为request和response读取完以后Position到最后一个位置,交给下一个方法处理的时候就会读不到内容了。 81 */ 82 stream.Position = 0; 83 return result; 84 } 85 }