前言

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

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

  • 异常中间件
  • 异常过滤器

介绍

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

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

  • UserFriendlyException(友好异常)
  • MasaException(框架异常)

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

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

快速入门

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

异常中间件

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

  1. 新建ASP.NET Core 空项目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 //提供全局异常过滤器
  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.GlobalFilterDemo
cd 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(不记录日志)

常见问题

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

Q:

  1. 检查是否指定了自定义异常处理的Handler,并且当前异常已经被自定义异常处理程序处理(ExceptionHandled = true)
  2. 检查当前异常类型是否配置了指定的日志等级,且当前日志等级小于默认记录日志的等级

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

Q: 未注入到服务集合且没有指定使用指定的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.EShop:https://github.com/masalabs/MASA.EShop

MASA.Blazor:https://github.com/BlazorComponent/MASA.Blazor

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

MasaFramework -- 异常处理的更多相关文章

  1. 关于.NET异常处理的思考

    年关将至,对于大部分程序员来说,马上就可以闲下来一段时间了,然而在这个闲暇的时间里,唯有争论哪门语言更好可以消磨时光,估计最近会有很多关于java与.net的博文出现,我表示要作为一个吃瓜群众,静静的 ...

  2. 基于spring注解AOP的异常处理

    一.前言 项目刚刚开发的时候,并没有做好充足的准备.开发到一定程度的时候才会想到还有一些问题没有解决.就比如今天我要说的一个问题:异常的处理.写程序的时候一般都会通过try...catch...fin ...

  3. 异常处理汇总 ~ 修正果带着你的Net飞奔吧!

    经验库开源地址:https://github.com/dunitian/LoTDotNet 异常处理汇总-服 务 器 http://www.cnblogs.com/dunitian/p/4522983 ...

  4. JavaScript var关键字、变量的状态、异常处理、命名规范等介绍

    本篇主要介绍var关键字.变量的undefined和null状态.异常处理.命名规范. 目录 1. var 关键字:介绍var关键字的使用. 2. 变量的状态:介绍变量的未定义.已定义未赋值.已定义已 ...

  5. IL异常处理

    异常处理在程序中也算是比较重要的一部分了,IL异常处理在C#里面实现会用到一些新的方法 1.BeginExceptionBlock:异常块代码开始,相当于try,但是感觉又不太像 2.EndExcep ...

  6. Spring MVC重定向和转发以及异常处理

    SpringMVC核心技术---转发和重定向 当处理器对请求处理完毕后,向其他资源进行跳转时,有两种跳转方式:请求转发与重定向.而根据要跳转的资源类型,又可分为两类:跳转到页面与跳转到其他处理器.对于 ...

  7. 【repost】JS中的异常处理方法分享

    我们在编写js过程中,难免会遇到一些代码错误问题,需要找出来,有些时候怕因为js问题导致用户体验差,这里给出一些解决方法 js容错语句,就是js出错也不提示错误(防止浏览器右下角有个黄色的三角符号,要 ...

  8. 札记:Java异常处理

    异常概述 程序在运行中总会面临一些"意外"情况,良好的代码需要对它们进行预防和处理.大致来说,这些意外情况分三类: 交互输入 用户以非预期的方式使用程序,比如非法输入,不正当的操作 ...

  9. 关于bug分析与异常处理的一些思考

    前言:工作三年了,工作内容主要是嵌入式软件开发和维护,用的语言是C,毕业后先在一家工业自动化控制公司工作两年半,目前在一家医疗仪器公司担任嵌入式软件开发工作.软件开发中,难免不产生bug:产品交付客户 ...

随机推荐

  1. 【web自动化测试】playwright安装失败怎么办

    在安装中,如果没有输入 playwright install, 则不会安装浏览器,运行 playwright codegen 时会报浏览器找不到的错误: "chromium" br ...

  2. GreatSQL特性介绍及前景展望 | 数据技术嘉年华2021分享PPT发布

    欢迎来到 GreatSQL社区分享的MySQL技术文章,如有疑问或想学习的内容,可以在下方评论区留言,看到后会进行解答 GreatSQL社区原创内容未经授权不得随意使用,转载请联系小编并注明来源. 全 ...

  3. 前端架构-分层而治,铁打的MV流水的C

    大家好,我是Eluxjs的作者,Eluxjs是一套基于"微模块"和"模型驱动"的跨平台.跨框架『同构方案』,欢迎了解... 文前声明,以下推断和结论纯属个人探索 ...

  4. 「题解报告」P3354

    P3354 题解 题目传送门 一道很恶心的树形dp 但是我喜欢 题目大意: 一片海旁边有一条树状的河,入海口有一个大伐木场,每条河的分叉处都有村庄.建了伐木场的村庄可以直接处理木料,否则要往下游的伐木 ...

  5. OpenJudge 1.5.28 分离整数的各个数位

    28:分离整数的各个数位 总时间限制: 1000ms 内存限制: 65536kB 描述 给定一个整数,要求从个位开始分离出它的每一位数字. 输入 输入一个整数,整数在1到100000000之间. 输出 ...

  6. 区块相隔虽一线,俱在支付同冶熔,Vue3.0+Tornado6前后端分离集成Web3.0之Metamask区块链虚拟三方支付功能

    最近几年区块链技术的使用外延持续扩展,去中心化的节点认证机制可以大幅度改进传统的支付结算模式的经营效率,降低交易者的成本并提高收益.但不能否认的是,区块链技术也存在着极大的风险,所谓身怀利器,杀心自起 ...

  7. python随机值生成的常用方法

    一.随机整数1.包含上下限:[a, b] import random #1.随机整数:包含上下限:[a, b] for i in range(10): print(random.randint(0,5 ...

  8. openstack 搭建详细步骤

    该博文转载于(https://www.cnblogs.com/whwh/p/16200004.html) 一.openstack单点部署 1.配置虚拟机NAT网络连接 查看vmware的NAT网络默认 ...

  9. Order Siblings by 排序

    在层次查询中,如果想让"亲兄弟"按规矩进行升序排序就需要使用ORDER SIBLINGS BY 这个特定的排序语句,若要降序输出可以在其后添加DESC关键字. 通过这个实验给大家展 ...

  10. Linux_more_less总结

    先写结论 : less is more,使用less 优于使用more more 和 less的区别 优于more不能后退,而less 就在其基础上增加了后退功能 less 可以使用键盘上的上下方向键 ...