webapi框架搭建系列博客
前言
上一篇我们已经完成了项目的日志管理,在项目开发中日志会经常记录程序中的异常,供后续问题排查使用。本篇讲如何在webapi里加入异常处理机制。
目的和原则
1、程序任何地方都不能catch掉异常,如果要catch也请重新throw异常或是将异常记录到日志里。避免异常被“吃掉“,导致无法排查程序的bug。
2、webapi接口的”请求成功“和”请求失败“以一定的标准规范提供给外部
我的规范为:
所有的成功请求返回200(response的status为200),返回的结果以json封装
所有的失败请求返回非200,错误以json返回,错误的内容为Message的值,如{"Message":"这里是异常的内容描述"}
3、如果为已知异常(即我们代码里写的throw异常)可简单的记录到日志,但如果是未知异常(我们不知道是哪里抛出的异常,往往也是程序的bug)则记录到特殊的日志文件里,如上篇的log/error目录下。
参考
微软webapi异常处理:https://docs.microsoft.com/zh-cn/aspnet/web-api/overview/error-handling/
步骤如下
自定义异常类
此异常类即是”已知“异常,约定程序里我们自己抛出的异常都用此类,Message属性记录异常的描述信息
using System; using System.Runtime.Serialization; namespace webapi.Exceptions { public class KnownException : Exception { public KnownException():base() { } public KnownException(string message) : base(message) { } public KnownException(string message, Exception innerException) : base(message, innerException) { } protected KnownException(SerializationInfo info, StreamingContext context) : base(info, context) { } } }
创建webapi异常过滤器
创建WebApiExceptionFilterAttribute类,代码如下
using System.Net; using System.Net.Http; using System.Web.Http.Filters; using log4net; namespace webapi.Exceptions { 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()); if (exception is KnownException) { log.Debug(logMessage); } else { log.Error(logMessage, exception); } actionExecutedContext.Response = actionExecutedContext.Request.CreateErrorResponse(HttpStatusCode.BadRequest, exceptionMessage); } } }
在webapi里使用异常过滤最简单的方法就是继承自ExceptionFilterAttribute类,并重写OnException方法并写入自己的异常处理逻辑
将异常过滤器加到webapi里
修改之前的WebApiConfig.OwinWebApiConfiguration方法,代码如下
/// <summary> /// 返回webapi的httpconfiguration配置 /// 用于webapi应用于owin技术时使用 /// </summary> /// <returns></returns> public static HttpConfiguration OwinWebApiConfiguration(HttpConfiguration config) { config.MapHttpAttributeRoutes();//开启属性路由 config.Routes.MapHttpRoute( name: "DefaultApi", routeTemplate: "api/{controller}/{id}", defaults: new { id = RouteParameter.Optional } ); config.Filters.Add(new WebApiExceptionFilterAttribute()); return config; }
增加的只是config.Filters.Add(new WebApiExceptionFilterAttribute());这一句
测试结果
创建用于测试的控制器,写两个接口分别模拟程序bug抛出的未知异常和自己抛出的已知异常
using System; using System.Web.Http; using webapi.Exceptions; namespace webapi.example { [RoutePrefix("api/exceptionTest")] public class ExceptionTestController : ApiController { /// <summary> /// 模拟程序bug抛出的异常 /// </summary> /// <returns></returns> [Route("unknown"),HttpGet] public IHttpActionResult UnKnow() { throw new Exception("未知的异常"); } /// <summary> /// 模拟主动抛出的业务异常 /// </summary> /// <returns></returns> [Route("known"), HttpGet] public IHttpActionResult Know() { throw new KnownException("已知的异常"); } } }
返问两个接口会得到内容为{"Message":"xxx"},status为400的respose,和我们的约定规则一样。