ASP.NET WebApi项目框架搭建(五):异常处理

一、前言

目的和原则

  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.输入一个不存在的请求:

 

posted @ 2020-04-22 22:25  HuTiger  阅读(1588)  评论(0编辑  收藏  举报