写点什么

MASA Framework 的异常处理

  • 2022-10-14
    浙江
  • 本文字数:3363 字

    阅读完需:约 11 分钟

前言


在程序设计中,我们会遇到各种各样的异常问题,一个异常处理不仅仅可以帮助开发者快速的定位问题,也可以给用户更好的使用体验,那么我们在 AspNetCore 项目中如何捕获以及处理异常呢?


而对应 AspNetCore 程序,我们有两种异常处理方案,它们分别是:


  • 异常中间件

  • 异常过滤器

介绍


Masa Framework作为一个框架,它为开发者以及用户提供更好的开发体验和使用体验的异常处理功能


Masa.Utils.Exceptions 中定义了两种异常类


  • UserFriendlyException(友好异常)

  • MasaException(框架异常)


并提供了两种异常处理方案,那接下来就让我们看看它们是如何使用的



根据需要自行选择一种方案使用即可

快速入门


项目基于.NET6.0 创建,必须安装所必须的环境


异常中间件


基于中间件实现的全局异常处理,用于捕捉应用程序异常,并将异常信息处理后返回


  1. 新建 ASP.NETCore 空项目Assignment.GlobalExceptionDemo,并安装Masa.Utils.Exceptions


dotnet new web -o Assignment.GlobalExceptionDemocd Assignment.GlobalExceptionDemo
dotnet add package Masa.Utils.Exceptions --version 0.6.0-rc.3 //提供全局异常过滤器
复制代码


  1. 新建用户类User


public class User{    public string Name { get; set; }
public int Age { get; set; }}
复制代码


  1. 使用全局异常,修改Program


//支持处理自定义异常app.UseMasaExceptionHandler(options =>{    //支持处理自定义异常    options.ExceptionHandler = context =>    {        if (context.Exception is ArgumentNullException ex)        {            context.ToResult($"{ex.ParamName}不能为空");        }    };});
复制代码


  1. 新增注册用户方法(用于自定义抛出异常)


app.MapPost("/register", (User user) =>{    if (string.IsNullOrEmpty(user.Name))        throw new ArgumentNullException(nameof(user.Name));    //todo: Impersonate a registered user});
复制代码


更多使用技巧可查看

异常过滤器


基于 MVC 的全局异常过滤器,用于捕捉应用程序异常,并将异常信息处理后返回


  1. 新建 ASP.NET Core 空项目Assignment.GlobalFilterDemo,并安装Masa.Utils.Exceptions


dotnet new web -o Assignment.GlobalFilterDemocd Assignment.GlobalFilterDemo
dotnet add package Masa.Utils.Exceptions --version 0.6.0-rc.3 //提供全局异常过滤器
复制代码


  1. 新建用户类User


public class User{    public string Name { get; set; }
public int Age { get; set; }}
复制代码


  1. 使用全局异常过滤器,修改Program


builder.Services    .AddMvc()    //使用MasaException    .AddMasaExceptionHandler(options =>    {        options.ExceptionHandler = context =>        {            if (context.Exception is ValidationException ex)            {                string message = ex.Errors.Select(error => error.ErrorMessage).FirstOrDefault()!;                context.ToResult(message);            }        };    });
复制代码


  1. 新增注册用户方法,用于自定义抛出异常


[ApiController][Route("[Action]")]public class UserController : ControllerBase{    [HttpPost]    public void Register(User user)    {        if (string.IsNullOrEmpty(user.Name))            throw new ArgumentNullException(nameof(user.Name));
//todo: Impersonate a registered user }}
复制代码


那我们如何确定异常已经处理完成了呢?这就需要经过以下这个步骤

验证全局异常处理


分别启用使用异常中间件的项目以及异常过滤器的项目,用 Postman 或者通过 Swagger 分别请求两个项目的注册用户接口,其中Name为空,可得到以下提示,则代表全局异常处理成功



以上是常规化的异常处理方式,如果遇到自定义异常我们该如何解决?


让我们接着往下看

进阶


不论是通过中间件还是过滤器来处理全局异常,我们都支持自定义异常处理,我们首先来看一下异常的处理流程



根据流程图可以直观的了解到,只要使用了Masa提供的异常处理,哪怕我们不自定义异常,框架也会帮助我们按照无自定义异常流程默认处理异常信息,但如果我们希望对特定的异常做出特定的响应,那么就需要我们自定义异常

自定义异常


自定义异常支持三种方式


以中间件为例:


方案一. 通过配置ExceptionHandler(异常处理),修改Program.cs


app.UseMasaExceptionHandler(options =>{    options.ExceptionHandler = context =>    {        // 根据context.Exception判断异常类型,并通过context.ToResult()输出响应内容        if (context.Exception is ArgumentNullException ex)        {            context.ToResult($"{ex.ParamName}不能为空");        }    };});
复制代码


方案二. 通过自定义 ExceptionHandler,并注册到服务集合


  1. 自定义异常处理类ExceptionHandler,并继承IMasaExceptionHandler


/// <summary>/// 构造函数参数需支持从IOC获取/// </summary>public class ExceptionHandler : IMasaExceptionHandler{    public void OnException(MasaExceptionContext context)    {        if (context.Exception is ArgumentNullException ex)        {            context.ToResult($"{ex.ParamName}不能为空");        }    }}
复制代码


  1. 使用指定的异常 Handler,修改Program.cs



builder.Services.AddSingleton<IMasaExceptionHandler, ExceptionHandler>();//注册自定义异常
var app = builder.Build();app.UseMasaExceptionHandler();// 在Program中执行异常处理程序
复制代码


方案三. 通过自定义 ExceptionHandler 并指定 ExceptionHandler 来实现


  1. 自定义异常处理类ExceptionHandler,并继承IMasaExceptionHandler


/// <summary>/// 构造函数参数需支持从IOC获取/// </summary>public class ExceptionHandler : IMasaExceptionHandler{    public void OnException(MasaExceptionContext context)    {        if (context.Exception is ArgumentNullException ex)        {            context.ToResult($"{ex.ParamName}不能为空");        }    }}
复制代码


  1. 使用指定的异常 Handler,修改Program.cs


app.UseMasaExceptionHandler(options => options.UseExceptionHanlder<ExceptionHandler>());//指定使用特定的异常处理程序
复制代码


上述三种方案任选其一即可,提供的功能时一样的,仅仅是写法不同

修改HttpStatusCode状态码

MasaExceptionContext默认提供了ToResult方法支持输入响应内容,状态码(默认: 299),内容类型 (默认:text/plain; charset=utf-8),我们可以根据自己的实际情况调用传参即可

修改日志级别

异常类型为UserFriendlyException的默认日志等级为Information,其余异常的日志等级为Error,那么如果我想修改对应异常的日志等级应该怎么做?


配置异常日志关系:


builder.Services.Configure<MasaExceptionLogRelationOptions>(options =>{    options.MapLogLevel<ArgumentNullException>(LogLevel.None);});
复制代码


按照此方式,可以将类型为 ArgumentNullException 异常的日志等级设置为 None(不记录日志)

常见问题


Q1: 为什么使用全局异常后没有记录日志?


A1:

  1. 检查是否指定了自定义异常处理的 Handler,并且当前异常已经被自定义异常处理程序处理(ExceptionHandled = true)

  2. 检查当前异常类型是否配置了指定的日志等级,且当前日志等级小于默认记录日志的等级


Q2: 实现IMasaExceptionHandler后,为什么发生异常后没有进入OnException


A2: 未注入到服务集合且没有指定使用指定的 ExceptionHanlder


  • 自定义异常 Handler


public class ExceptionHandler : IMasaExceptionHandler{    public void OnException(MasaExceptionContext context)    {        throw new NotImplementedException();    }}    
复制代码


可参考自定义异常中的方案二或者方案三修改即可

总结


Masa 提供的全局异常中间件,对自定义异常的扩展支持较好,并且后续Masa Framework支持 I18n 后,全局异常也将增加 I18n 支持, 届时全局异常会更加方便

本章源码


Assignment13


https://github.com/zhenlei520/MasaFramework.Practice

开源地址


MASA.Framework:https://github.com/masastack/MASA.Framework


如果你对我们的 MASA Framework 感兴趣,无论是代码贡献、使用、提 Issue,欢迎联系我们


  • WeChat:MasaStackTechOps

  • QQ:7424099

发布于: 刚刚阅读数: 5
用户头像

还未添加个人签名 2021-10-26 加入

MASA技术团队官方账号,我们专注于.NET现代应用开发解决方案,Wechat:MasaStackTechOps ,Website:www.masastack.com

评论

发布
暂无评论
MASA Framework的异常处理_.net_MASA技术团队_InfoQ写作社区