• 区分真异常和逻辑异常


    异常处理技巧

    • 用特定的异常类或接口表示业务处理异常
    • 为业务逻辑异常定义全局错误码
    • 为未知异常定义特点的输出信息和错误码
    • 对于已知业务逻辑异常响应 HTTP 200(监控系统友好)
    • 对于未预见的异常响应 HTTP 500
    • 为所有的异常记录详细的日志

    错误处理页:

    Asp.Net Core 在开发情况下为我们启用了错误处理中间件。


     但在生产模式中我们应该关闭掉,我们正常处理我们错误的方式:

    • 自定义错误页

    准备工作:

    定义一个IknowException接口:

    public interface IKnownException
    {
        public string Message { get; }
        public int ErrorCode { get; }
        public object[] ErrorData { get; }
    }

    定义一个KnowException类并实现IknowException接口:

    public class KnownException : IKnownException
    {
        public string Message { get; private set; }
        public int ErrorCode { get; private set; }
        public object[] ErrorData { get; private set; }
    
        public static readonly IKnowException UnKnownException = new KnowException { Message = "未知的错误", ErrorCode = 9999};
    
        public static IKnownException FromKnownException(IKnowException exception)
        {
            return new KnownException
                {Message = exception.Message, ErrorCode = exception.ErrorCode, ErrorData = exception.ErrorData};
        }
    }

    我们为什么要定义这样一个类型,是因为通常情况下,系统发生的异常和业务逻辑的异常是不同的。

    系统发生的异常可能为网络出现异常,数据库连接出现异常,Redis的连接出现了异常等等。

    比如说业务逻辑异常为输入的参数或订单的状态不符合条件,当前余额不足这种信息,我们的处理方式为对不同的逻辑输出不同的业务对象,或输出一个异常,用异常来承载我们的逻辑的特殊分支。所以我们需要识别哪些是系统异常哪些是业务逻辑异常。

    ErrorController:

    public class ErrorController : Controller
    {
        [Route("/error")]
        public IActionResult Index()
        {
            var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>();
    
            var ex = exceptionHandlerPathFeature?.Error;
           
            if (!(ex is IKnowException knowException))
            {
                var logger = HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
                if (ex != null) logger.LogError(ex, ex.Message);
                knowException = KnowException.UnKnowException;
            }
            else
            {
                knowException = KnowException.FromKnowException(knowException);
            }
    
            return View(knowException);
        }
    }

    对于否为未知的异常,我们不应该把我们错误的异常完整的输出给客户端,应该传递一个特殊的错误信息(在此我们传递回错误信息为未知的错误,错误号为9999的一个错误信息)。

    但在日志系统中我们应记录完整的错误信息。


    • 使用代理方法

    在StartUp类中使用中间件委托代理处理异常

    app.UseExceptionHandler(errApp =>
    {
        errApp.Run(async context =>
        {
            var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
    
            var ex = exceptionHandlerPathFeature?.Error;
    
            if (!(ex is IKnownException knowException))
            {
                var logger = context.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
                if (ex != null) logger.LogError(ex, ex.Message);
                knowException = KnownException.UnKnowException;
                context.Response.StatusCode = StatusCodes.Status500InternalServerError;
            }
            else
            {
                knowException = KnownException.FromKnowException(knowException);
                context.Response.StatusCode = StatusCodes.Status200OK;
            }
    
            var jsonOptions = context.RequestServices.GetService<IOptions<JsonOptions>>();
            context.Response.ContentType = "application/json; charset=utf-8";
            await context.Response.WriteAsync(
                System.Text.Json.JsonSerializer.Serialize(knowException,
                    jsonOptions.Value.JsonSerializerOptions));
        });
    });

    这里对于未知的系统异常我们应该输出 HTTP 500, 而对于业务逻辑的异常输出 HTTP 200。是因为监控系统会对这些错误进行识别,当识别到响应为500的比例较高的情况下,我们认为系统的可用性出现问题。对已知的业务逻辑错误使用200处理是正常的行为。这样使得告警系统更加灵敏,防止业务逻辑的异常去干扰告警系统。

    上述中间件可捕捉我们自定义的异常:

    public class MyServerException : Exception, IKnownException
    {
        public MyServerException(string message, int errorCode, params object[] errorData) : base(message)
        {
            this.ErrorCode = errorCode;
            this.ErrorData = errorData;
        }
    
        public int ErrorCode { get; private set; }
        public object[] ErrorData { get; private set; }
    }

    抛出异常:

    浏览器响应:

     


    • 异常过滤器(作用在MVC体系之下而不是中间件)

    准备工作: 定义自己的异常过滤器,需要实现IExceptionFilter接口。

    public class MyExceptionFilter : IExceptionFilter
    {
        public void OnException(ExceptionContext context)
        {
            IKnownException knownException = context.Exception as IKnownException;
            if (knownException == null)
            {
                var logger = context.HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
                logger.LogError(context.Exception, context.Exception.Message);
                knownException = KnownException.UnKnowException;
                context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
            }
            else
            {
                knownException = KnownException.FromKnowException(knownException);
                context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
            }
            context.Result = new JsonResult(knownException)
            {
                ContentType = "application/json; charset=utf-8"
            };
        }
    }

    使用场景一般是对于控制器的特殊异常处理,或当中间件有一套处理异常方式时,需要另外一个异常处理方式时也可用此方式。使用时在ConfigureServices里为MVC模式添加Filter即可。

    services.AddMvc(options =>
                {
                    options.Filters.Add<MyExceptionFilter>();
                }).AddJsonOptions(options =>
                {
                    options.JsonSerializerOptions.Encoder = JavaScriptEncoder.Default;
                });

    对于MVC还有一种使用Attribute的方式:需实现ExceptionFilterAttribute接口

    public class MyExceptionFilterAttribute : ExceptionFilterAttribute
    {
        public override void OnException(ExceptionContext context)
        {
            IKnownException knownException = context.Exception as IKnownException;
            if (knownException == null)
            {
                var logger = context.HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
                logger.LogError(context.Exception, context.Exception.Message);
                knownException = KnownException.UnKnowException;
                context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
            }
            else
            {
                knownException = KnownException.FromKnowException(knownException);
                context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
            }
            context.Result = new JsonResult(knownException)
            {
                ContentType = "application/json; charset=utf-8"
            };
        }
    }

    这样可以将Attribute应用到你想进行异常处理的控制器即可。

  • 相关阅读:
    SHELL变量
    LA 2797
    计算几何-圆 模板 训练指南267
    hdu 2553 八皇后问题 基础
    CodeForces 557C Arthur and Table STL的使用
    LA 3263 好看的一笔画 欧拉几何+计算几何模板
    UVA 11178 Morley's Theorem 计算几何模板
    poj 1113
    poj 2187 Beauty Contest 凸包模板+求最远点对
    hdu 1081 dp问题:最大子矩阵和
  • 原文地址:https://www.cnblogs.com/Xieyiincuit/p/13997417.html
Copyright © 2020-2023  润新知