Fork me on GitHub

asp.net core web api之异常

  官方建议用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());
});

 

  想要更快更方便的了解相关知识,可以关注微信公众号 
 

 

 

posted @ 2022-01-30 18:55  桂素伟  阅读(75)  评论(0编辑  收藏  举报