异常处理中间件:区分真异常与逻辑异常 电脑版发表于:2020/7/30 15:10 ![.netcore](https://img.tnblog.net/arcimg/hb/c857299a86d84ee7b26d181a31e58234.jpg ".netcore") >#异常处理中间件:区分真异常与逻辑异常 [TOC] <br/> 处理异常的方式 ------------ - 异常处理页 - 异常处理匿名委托方法 - IExceptionFilter - ExceptionFilterAttribute Demo ------------ >###项目结构 ![项目结构](https://img.tnblog.net/arcimg/hb/e1d528d2eab244ed88ff22bc51cfa971.jpg "项目结构") >###创建相关Model <br/> ><font style="color:#f39c12;font-weight:bold;">IKnownException.cs</font> ```csharp public interface IKnownException { public string Message { get; } public int ErrorCode { get; } public object[] ErrorData { get; } } ``` ><font style="color:#2ecc71;font-weight:bold;">KnownException.cs</font> ```csharp public class KnownException : IKnownException { public string Message { get; private set; } public int ErrorCode { get; private set; } public object[] ErrorData { get; private set; } public readonly static IKnownException Unknown = new KnownException { Message = "未知错误", ErrorCode = 9999 }; public static IKnownException FromKnownException(IKnownException exception) { return new KnownException { Message = exception.Message, ErrorCode = exception.ErrorCode, ErrorData = exception.ErrorData }; } } ``` ><font style="color:#2ecc71;font-weight:bold;">MyExceptionFilter.cs</font> ```csharp 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.Unknown; context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; } else { knownException = KnownException.FromKnownException(knownException); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; } context.Result = new JsonResult(knownException) { ContentType = "application/json; charset=utf-8" }; } } ``` ><font style="color:#2ecc71;font-weight:bold;">MyServerException.cs</font> ```csharp 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; } } ``` >创建错误处理<font style="color:#2ecc71;font-weight:bold;">MyExceptionFilterAttribute.cs</font>过滤器 ```csharp 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.Unknown; context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError; } else { knownException = KnownException.FromKnownException(knownException); context.HttpContext.Response.StatusCode = StatusCodes.Status200OK; } context.Result = new JsonResult(knownException) { ContentType = "application/json; charset=utf-8" }; } } ``` >创建错误处理<font style="color:#2ecc71;font-weight:bold;">ErrorController.cs</font>控制器 ```csharp [AllowAnonymous] public class ErrorController : Controller { [Route("/error")] public IActionResult Index() { var exceptionHandlerPathFeature = HttpContext.Features.Get<IExceptionHandlerPathFeature>(); var ex = exceptionHandlerPathFeature?.Error; var knownException = ex as IKnownException; if (knownException == null) { var logger = HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>(); logger.LogError(ex, ex.Message); knownException = KnownException.Unknown; } else { knownException = KnownException.FromKnownException(knownException); } return View(knownException); } } ``` >###创建报错方法 <br/> ><font style="color:#2ecc71;font-weight:bold;">WeatherForecastController.cs</font> ```csharp [ApiController] [Route("[controller]")] public class WeatherForecastController : ControllerBase { [HttpGet] public int Get() { throw new MyServerException("服务出错了", 65); } } ``` >###通过控制器处理错误 <br/> >修改<font style="color:#2ecc71;font-weight:bold;">Startup.cs</font> 的 <font style="color:#9b59b6;font-weight:bold;">Configure</font>方法,添加处理异常路径 ```csharp public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseExceptionHandler("/error"); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } ``` >注意请修改<font style="color:#9b59b6;font-weight:bold;">ConfigureServices</font>方法 ```csharp public void ConfigureServices(IServiceCollection services) { services.AddMvc();//.AddControllers(); } ``` >运行结果 ![](https://img.tnblog.net/arcimg/hb/ba56325804884b9a9c830f50bea20c47.jpg) >###通过委托的方式处理错误 <br/> >修改<font style="color:#2ecc71;font-weight:bold;">Startup.cs</font> 的 <font style="color:#9b59b6;font-weight:bold;">Configure</font>方法 ```csharp public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { app.UseExceptionHandler(errApp => { errApp.Run(async context => { var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>(); IKnownException knownException = exceptionHandlerPathFeature.Error as IKnownException; if (knownException == null) { var logger = context.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>(); logger.LogError(exceptionHandlerPathFeature.Error, exceptionHandlerPathFeature.Error.Message); knownException = KnownException.Unknown; context.Response.StatusCode = StatusCodes.Status500InternalServerError; } else { knownException = KnownException.FromKnownException(knownException); 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(knownException, jsonOptions.Value.JsonSerializerOptions)); }); }); app.UseRouting(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } ``` >运行结果(这里有部分乱码) ![](https://img.tnblog.net/arcimg/hb/7f3e353963c6488cbb99c3e82a8c32d1.jpg) >###通过过滤器的方式处理错误 <br/> ><font style="color:#9b59b6;font-weight:bold;">Configure</font>方法 ```csharp public void ConfigureServices(IServiceCollection services) { services.AddMvc(mvcOptions => { //mvcOptions.Filters.Add<MyExceptionFilter>(); mvcOptions.Filters.Add<MyExceptionFilterAttribute>(); }).AddJsonOptions(jsonoptions => { jsonoptions.JsonSerializerOptions.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping; }); } ``` >运行结果 ![](https://img.tnblog.net/arcimg/hb/b0a0a69845884a6ea07489ff13454ad7.jpg) 异常处理技巧 ------------ - 用特定的异常类活接口表示业务逻辑异常 - 为业务逻辑异常定义全局错误码 - 为未知异常定义特定的输出信息和错误码 - 对于已知业务逻辑异常响应 HTTP 200(监控系统友好) - 对于未预见的异常响应 HTTP 500 - 为所有的异常记录详细的日志