我们写代码过程中,异常是较为常见的,如果用户直接看到我们未处理好的异常的话,用户体验是非常不好的,所以,我们需要了解一下如何来处理程序中的异常。
这里我新建了一个名为ExceptionController
的控制器,加入几行简单的代码
1 2 3 4 5 6 7 8 9 [Route("api/[controller]")] [ApiController] public class ExceptionController : ControllerBase { [HttpGet] public string Test() { throw new ArgumentException("参数有误"); } }
这时候来我们Debug
模式看看程序是什么现象, 通过Swagger或者是浏览器直接访问,就可以看到上面这些错误提示,并且在控制台也会记录相关错误信息
如果我们正式部署之后,服务端默认只会返回Status Code
为500
,其他信息则只能在后端控制台或者日志中才能看到。
这种方式显然是不友好的,所以,我们需要多异常进行处理
异常处理程序 当程序发生异常时,我们可以借助UseExceptionHandler
中间件来路由到相应的错误。这里,我们分别对开发环境和非开发环境创建一个路由,如:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 [Route("/error-local-development")] public IActionResult ErrorLocalDevelopment( [FromServices] IWebHostEnvironment webHostEnvironment) { if (webHostEnvironment.EnvironmentName != "Development") { throw new InvalidOperationException( "This shouldn't be invoked in non-development environments."); } var context = HttpContext.Features.Get<IExceptionHandlerFeature>(); return Problem( detail: context.Error.StackTrace, title: context.Error.Message); } [Route("/error")] public IActionResult Error() => Problem();
然后在Program.cs
中使用UseExceptionHandler
中间件
1 2 3 4 5 6 7 8 9 if (app.Environment.IsDevelopment()) { app.UseSwagger(); app.UseSwaggerUI(); app.UseExceptionHandler("/error-local-development"); } else { app.UseExceptionHandler("/error"); }
这时候,我们再来访问一次,
这样的相应格式,确实要比之前的要稍微直观一点,但是对于前端展示,还是一脸懵逼。
使用异常来修改响应 前面的方式,依然不够友好,官网说,可以通过Filter
来处理我们的异常,那我们就依样画葫芦,先新建一个名为HttpExceptionFilter
的类,然后继承IActionFilter, IOrderedFilter
这两个接口,IActionFilter
主要是处理异常,IOrderedFilter
则处理Filter
发处理顺序,因为框架中有其他的Filter
要运行,要让我们自定义的Filter
生效,那么就放置在最后,代码如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 public class HttpExceptionFilter : IActionFilter, IOrderedFilter { public int Order { get; } = int.MaxValue - 10; public void OnActionExecuted(ActionExecutedContext context) { context.Result = new ObjectResult("服务异常") { StatusCode = ((int)HttpStatusCode.BadGateway), }; context.ExceptionHandled = true; } public void OnActionExecuting(ActionExecutingContext context) { } }
在Program.cs
中添加Filter
1 builder.Services.AddControllers(options => options.Filters.Add(new HttpExceptionFilter()))
喏,这就是我们的实践成果,但是,对于WebApi来说,这种格式显然是要不得的,我们还需要统一一下返回格式,所以,我们对这个Filter
改造一下
1 2 3 4 5 6 7 8 9 10 public void OnActionExecuted(ActionExecutedContext context) { context.Result = new ObjectResult(new { Code = 500, Msg = "服务端异常" }) { StatusCode = ((int)HttpStatusCode.BadGateway), }; context.ExceptionHandled = true; }
这样子是要好一些了,但是所有的异常都提示这个,显然是不行的,所以,我们需要自定义错误类,这里新建一个名为BizException
的类,继承Exception
,
1 2 3 4 5 6 7 8 9 public class BizException: Exception { public int Status { get; set; } = 201; public string Tips { get; set; } public BizException(string message) { Tips = message; } }
控制器中的访问使用自定义的异常类
1 2 3 4 [HttpGet] public string Test() { throw new BizException("参数有误"); }
在Filter
中的OnActionExecuted
方法中针对不同的异常来处理返回信息
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 public void OnActionExecuted(ActionExecutedContext context) { if (context.Exception is BizException) { BizException bizException = (BizException)context.Exception; context.Result = new ObjectResult(new { Code = bizException.Status, Msg = bizException.Tips }) { StatusCode = ((int)HttpStatusCode.BadGateway), }; } else { context.Result = new ObjectResult(new { Code = 500, Msg = "服务端异常" }) { StatusCode = ((int)HttpStatusCode.BadGateway), }; } context.ExceptionHandled = true; }
这样一来,我们的异常则会统一处理了,前端是可以直观的看到信息提示了,那么后端也得把异常信息记录下来,不然到时候出错了,不是前端一脸懵逼了,该是后端开始懵逼了。处理使用Filter
来处理异常,还可以使用自定义中间件来处理。
这里,我们简单的了解了通过Filter
来处理异常,本文只是简单的示例,实际开发中则需要更强的逻辑,下面,我继续学习ASP.NET Core 中间件