前言
- 
	异常中间件 
- 
	异常过滤器 
介绍
Masa Framework作为一个框架,它为开发者以及用户提供更好的开发体验和使用体验的异常处理功能Masa.Utils.Exceptions (MASA.Framework/src/Utils/Masa.Utils.Exceptions at main · masastack/MASA.Framework (github.com)中定义了两种异常类.- 
	UserFriendlyException(友好异常)
- 
	MasaException(框架异常)
根据需要自行选择一种方案使用即可 
快速入门
- 
	安装.NET 6.0 
- 
	安装地址(https://dotnet.microsoft.com/zh-cn/download/dotnet/6.0) 
异常中间件
基于中间件实现的全局异常处理,用于捕捉应用程序异常,并将异常信息处理后返回
Assignment.GlobalExceptionDemo,并安装Masa.Utils.Exceptions
dotnet new web -o Assignment.GlobalExceptionDemo
cd Assignment.GlobalExceptionDemo
dotnet add package Masa.Utils.Exceptions --version 0.6.0-rc.3 //提供全局异常过滤器
User
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}
Program
//支持处理自定义异常
app.UseMasaExceptionHandler(options =>
{
    //支持处理自定义异常
    options.ExceptionHandler = context =>
    {
        if (context.Exception is ArgumentNullException ex)
        {
            context.ToResult($"{ex.ParamName}不能为空");
        }
    };
});
4.新增注册用户方法(用于自定义抛出异常)
app.MapPost("/register", (User user) =>
{
    if (string.IsNullOrEmpty(user.Name))
        throw new ArgumentNullException(nameof(user.Name));
    //todo: Impersonate a registered user
});
更多使用技巧可查看(https://github.com/masastack/MASA.Utils/blob/main/src/Masa.Utils.Exceptions/README.zh-CN.md)
异常过滤器
基于MVC的全局异常过滤器,用于捕捉应用程序异常,并将异常信息处理后返回
Assignment.GlobalFilterDemo,并安装Masa.Utils.Exceptions
dotnet new web -o Assignment.GlobalFilterDemo
cd Assignment.GlobalFilterDemo
dotnet add package Masa.Utils.Exceptions --version 0.6.0-rc.3 //提供全局异常过滤器
User
public class User
{
    public string Name { get; set; }
    public int Age { get; set; }
}
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);
            }
        };
    });
[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
    }
}
那我们如何确定异常已经处理完成了呢?这就需要经过以下这个步骤
验证全局异常处理
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,并注册到服务集合
ExceptionHandler,并继承IMasaExceptionHandler
/// <summary>
/// 构造函数参数需支持从IOC获取
/// </summary>
public class ExceptionHandler : IMasaExceptionHandler
{
    public void OnException(MasaExceptionContext context)
    {
        if (context.Exception is ArgumentNullException ex)
        {
            context.ToResult($"{ex.ParamName}不能为空");
        }
    }
}
Program.cs
builder.Services.AddSingleton<IMasaExceptionHandler, ExceptionHandler>();//注册自定义异常
var app = builder.Build();
app.UseMasaExceptionHandler();// 在Program中执行异常处理程序
方案三. 通过自定义ExceptionHandler并指定ExceptionHandler来实现
ExceptionHandler,并继承IMasaExceptionHandler
/// <summary>
/// 构造函数参数需支持从IOC获取
/// </summary>
public class ExceptionHandler : IMasaExceptionHandler
{
    public void OnException(MasaExceptionContext context)
    {
        if (context.Exception is ArgumentNullException ex)
        {
            context.ToResult($"{ex.ParamName}不能为空");
        }
    }
}
Program.csapp.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.检查当前异常类型是否配置了指定的日志等级,且当前日志等级小于默认记录日志的等级
IMasaExceptionHandler后,为什么发生异常后没有进入OnException- 
	自定义异常Handler 
public class ExceptionHandler : IMasaExceptionHandler
{
    public void OnException(MasaExceptionContext context)
    {
        throw new NotImplementedException();
    }
}
总结
Masa提供的全局异常中间件,对自定义异常的扩展支持较好,并且后续Masa Framework(https://github.com/masastack/MASA.Framework)支持I18n后,全局异常也将增加I18n支持, 届时全局异常会更加方便本章源码
Assignment13
https://github.com/zhenlei520/MasaFramework.Practice
开源地址
MASA.Framework:https://github.com/masastack/MASA.Framework
