官方建议用app.UseExceptionHandler("/error")来集中处理异常,本例是一个具体的应用。
比如项目中有一个ViewModel,要求Name最大长度为5
/// <summary> /// 用户模型 /// </summary> public class UserModel { /// <summary> /// ID /// </summary> public int ID { get; set; } /// <summary> ///名称 /// </summary> [MaxLength(5, ErrorMessage = "长度不能超过5")] public string Name { get; set; } }
在TestController中有两个Action,都有异常的机率,Get方法中,一个异常是系统内置的0被整除,另一个是我们自定义的业务层级异常(.NET架构小技巧(8)中有涉及);AddUser是有Model验证有可能Name超过5个字符后报异常。Error方法一个错误处理Action,根据上下文的异常来分流系统内置异常,还是自定业务异常。
/// <summary> /// get接口 /// </summary> /// <returns></returns> [HttpGet] public IActionResult Get() { var ran = new Random(); switch (ran.Next(1, 4)) { case 1: int i = 0; var j = 10 / i; return Ok(); case 2: throw new RegisteredException("这是一个错误"); default: return Ok(); } } /// <summary> /// 添加用户接口 /// </summary> /// <param name="user"></param> /// <returns></returns> [HttpPost("/adduser")] public IActionResult AddUser([FromBody] UserModel user) { return Ok(user); } /// <summary> /// 错误处理页 /// </summary> /// <returns></returns> [HttpGet("/error")] public IActionResult Error() { var context = HttpContext.Features.Get<IExceptionHandlerFeature>(); //如果是业务自定义异常,进行特殊处理 if (context.Error is DaMeiException) { return Problem(detail: context.Error.StackTrace, title: $"{context.Error.Message}", type: "HIS"); } else { return Problem(detail: context.Error.StackTrace, title: context.Error.Message); } }
层级异常类
using System; namespace WebApiError { /// <summary> /// 产品异常 /// </summary> public class DaMeiException : ApplicationException { /// <summary> /// /// </summary> /// <param name="message"></param> public DaMeiException(string message) : base(message) { } } /// <summary> /// His项目异常 /// </summary> public class HisException : DaMeiException { /// <summary> /// /// </summary> /// <param name="message"></param> public HisException(string message) : base(message) { } } /// <summary> /// Lis项目异常 /// </summary> public class LisException : DaMeiException { /// <summary> /// /// </summary> /// <param name="message"></param> public LisException(string message) : base(message) { } } /// <summary> /// 模块异常 /// </summary> public class RegisteredException : HisException { /// <summary> /// /// </summary> /// <param name="message"></param> public RegisteredException(string message) : base(message) { } } }
Error的Action之所有调用到,是因为Configure中添加如下代码,把所有异常交给"/error"来处理。
app.UseExceptionHandler("/error");
添加DaMeiProblemDetailsFactory来兼容各类异常,自定义异常和Model验证异常
using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Infrastructure; using Microsoft.AspNetCore.Mvc.ModelBinding; using System.Collections.Generic; namespace WebApiError { /// <summary> /// /// </summary> public class DaMeiProblemDetailsFactory : ProblemDetailsFactory { /// <summary> /// 处理业务错误 /// </summary> /// <param name="httpContext"></param> /// <param name="statusCode"></param> /// <param name="title"></param> /// <param name="type"></param> /// <param name="detail"></param> /// <param name="instance"></param> /// <returns></returns> public override ProblemDetails CreateProblemDetails(HttpContext httpContext, int? statusCode = null, string title = null, string type = null, string detail = null, string instance = null) { var problem = new ProblemDetails() { Title = string.IsNullOrEmpty(type) ? title : $"业务异常错误:{title}", Detail = detail, Status = statusCode, Instance = instance, Type = type }; return problem; } /// <summary> /// 处理model验证错误 /// </summary> /// <param name="httpContext"></param> /// <param name="modelStateDictionary"></param> /// <param name="statusCode"></param> /// <param name="title"></param> /// <param name="type"></param> /// <param name="detail"></param> /// <param name="instance"></param> /// <returns></returns> public override ValidationProblemDetails CreateValidationProblemDetails(HttpContext httpContext, ModelStateDictionary modelStateDictionary, int? statusCode = null, string title = null, string type = null, string detail = null, string instance = null) { var problem = new ValidationProblemDetails() { Title = "Model验证错误", Detail = detail, Status = statusCode, Instance = instance, Type = type }; foreach (var a in modelStateDictionary) { var errorList = new List<string>(); foreach (var error in a.Value.Errors) { errorList.Add(error.ErrorMessage); } problem.Errors.Add(new KeyValuePair<string, string[]>(a.Key, errorList.ToArray())); } return problem; } } }
在ConfigureServices中需要注入DaMeiProblemDetailsFactory
services.AddTransient<ProblemDetailsFactory, DaMeiProblemDetailsFactory>();
其实还可以用Action过滤器来统一管理异常
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace WebApiError { /// <summary> /// 自定义过滤器处理异常 /// </summary> public class DaMeiExceptionFilter : IActionFilter, IOrderedFilter { /// <summary> /// /// </summary> public int Order { get; } = int.MaxValue - 10; /// <summary> /// /// </summary> /// <param name="context"></param> public void OnActionExecuting(ActionExecutingContext context) { } /// <summary> /// /// </summary> /// <param name="context"></param> public void OnActionExecuted(ActionExecutedContext context) { if (context?.Exception != null) { context.Result = new ObjectResult(context.Exception.Message) { StatusCode = 500 }; context.ExceptionHandled = true; } } } }
另外一种方式是通过Action过滤器来处理
using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; namespace WebApiError { /// <summary> /// 自定义过滤器处理异常 /// </summary> public class DaMeiExceptionFilter : IActionFilter, IOrderedFilter { /// <summary> /// /// </summary> public int Order { get; } = int.MaxValue - 10; /// <summary> /// /// </summary> /// <param name="context"></param> public void OnActionExecuting(ActionExecutingContext context) { } /// <summary> /// /// </summary> /// <param name="context"></param> public void OnActionExecuted(ActionExecutedContext context) { if (context?.Exception != null) { if (context.Exception is DaMeiException) { context.Result = new ObjectResult(context.Exception.Message) { Value = $"业务异常:{ context.Exception.Message}", StatusCode = 500 }; } else { context.Result = new ObjectResult(context.Exception.Message) { Value = context.Exception.Message, StatusCode = 500 }; } context.ExceptionHandled = true; } } } }
添加全局过滤器
services.AddControllers(options => { options.Filters.Add(new DaMeiExceptionFilter()); });
想要更快更方便的了解相关知识,可以关注微信公众号