【5min+】AspNet Core中的全局异常处理
【5min+】AspNet Core中的全局异常处理
系列介绍
【五分钟的dotnet】是一个利用您的碎片化时间来学习和丰富.net知识的博文系列。它所包含了.net体系中可能会涉及到的方方面面,比如C#的小细节,AspnetCore,微服务中的.net知识等等。
5min+不是超过5分钟的意思,"+"是知识的增加。so,它是让您花费5分钟以下的时间来提升您的知识储备量。
正文
其实一说到AspNet Core里面的全局异常,其实大家都不会陌生。因为这玩意儿用的非常频繁,好的异常处理方案能够帮助开发者更快速的定位问题,也能够给用户更好的用户体验。
比如,当您访问到一个网页,突然,它喵的报错了!您没有看错,它报错了!!!然后显示了这样的一个错误页面:
请问,此刻电脑屏幕前的您会什么感受。(真想掏出那传说中的95级史诗巨剑!)
但是,假若我们稍微处理一下这个异常,比如用咱们腾讯爸爸的手段,换个皮肤:
用户马上就会想:“哎呀,错误就错误嘛,孰能无过,程序员锅锅也挺辛苦的。”
由此可见!!!全局异常的捕获和处理是有多么的重要。
AspNet Core 中的全局处理
IAsyncExceptionFilter
那么在AspNet Core中我们该如何捕获和处理异常呢? 可能很多同学都知道:IExceptionFilter 。 这个过滤器应该算是AspNet里面的老牌过滤器了,从很早就延续至今,它允许咱们捕获AspNet Core的控制器中的错误。不过,对于使用 IExceptionFilter,其实我更建议您考虑它的异步版本: IAsyncExceptionFilter。(别问为什么,问就是爱的供养)。
那么我们来看看该过滤器是怎么使用的呢? 下面以 IAsyncExceptionFilter 为例,对于同步版本其实也是一样的:
public class MyCustomerExceptionFilter : IAsyncExceptionFilter
{
public Task OnExceptionAsync(ExceptionContext context) { if (context.ExceptionHandled == false) { string msg = context.Exception.Message; context.Result = new ContentResult { Content = msg, StatusCode = StatusCodes.Status200OK, ContentType = "text/html;charset=utf-8" }; } context.ExceptionHandled = true; //异常已处理了 return Task.CompletedTask; }
}
上面咱们新建了一个自定义的异常过滤器,代码很简单,就是报错了之后依旧让Http返回状态码为200的结果。并且将错误信息返回到客户端。
然后还需要在 Startup.cs 中,告诉 MVC 咱们新加的这个过滤器:
services.AddControllers(options => options.Filters.Add(new MyCustomerExceptionFilter()));
然后就完了,是不是so easy? 来看看结果:
[HttpGet]
public IEnumerable Get()
{
throw new Exception("has error!");
}
如果不增加该过滤器,我们将得到Http状态码为500的响应。这对于某些不致命的意外操作来说,有点杀鸡用牛刀的感觉,对于前端用户来说也不是很友好(明明输错了一个字符,就直接被告知网站崩溃,并且出现乔殿下)。
而咱们捕获了异常,进行特殊处理之后就显得很友好了。(返回200,并且告诉用户输错了某字符等)。
在上面的代码中,您会看到有一行 context.ExceptionHandled = true;。注意!!! 这很关键,当您处理完异常之后,请记得将此属性更改为true,表明异常已经处理过了。如果不更改的话,嘿嘿🤪。会有什么结果呢? 请看下面↓
中间件处理异常
由于AspNet Core管道的层层传递的特点,咱们就有机会在管道中实现全局异常捕获。 新建一个中间件来试试吧:
public class MyExceptionMiddleware
{
private readonly RequestDelegate _next; public MyExceptionMiddleware(RequestDelegate next) { _next = next; } public async Task Invoke(HttpContext httpContext) { try { await _next(httpContext); } catch (Exception ex) { httpContext.Response.ContentType = "application/problem+json"; var title = "An error occured: " + ex.Message; var details = ex.ToString(); var problem = new ProblemDetails { Status = 200, Title = title, Detail = details }; //Serialize the problem details object to the Response as JSON (using System.Text.Json) var stream = httpContext.Response.Body; await JsonSerializer.SerializeAsync(stream, problem); } }
}
然后在 Startup.cs 中,注册管道:
app.UseMiddleware();
来看看效果:
还是原来的味道,还是熟悉的配方,爽歪歪!
管道的添加顺序决定了它的执行顺序,所以如果您想扩大异常捕获的范围,可以将该管道放置在 Configure 的第一行。 但是!! 您会发现,这个默认的AspNet Core项目不是已经在第一行弄了一个异常处理么? 我*&&……&。
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Error");
}
这行代码大家在初始化新AspNetCore项目时就会看到,也有可能您只有上半段,这和模板有关系。不过这都没有关系,它的作用就是捕获和处理异常而已。关于 UseDeveloperExceptionPage 该扩展咱们就不多说了,它的意思是:对于开发模式,一旦报错就会跳转到错误堆栈页面。 而第二个 UseExceptionHandler 就很有意思了,从它命名就可以看出,它肯定是个错误拦截程序。那么它和咱们自定义的异常处理管道有什么区别呢?
“不指定肯定有个默认吧!” 是的,它就是默认的错误处理。所以,它其实也是一个中间件,它的真身叫做 ExceptionHandlerMiddleware。在使用 UseExceptionHandler 方法时,我们可以选填各种参数。比如上方的代码,填入了 "/Error" 参数,表示当产生异常的时候,将定向到对应路径,此处就定位的是: "http://localhost:5001/Error" 。当然您也可以随意指定页面,比如 漂亮的乔殿下页面。😝
还有指定 ExceptionHandlerOptions 参数的方法,该参数是ExceptionHandlerMiddleware中间件的重要参数:
参数名 说明
ExceptionHandlingPath 重定向的路径,比如刚才的 ""/Error"" 实际上就是指定的该参数
ExceptionHandler 错误拦截处理程序
ExceptionHandler 允许我们在 ExceptionHandlerMiddleware 内部指定咱们自有的异常处理逻辑。而该参数的类型为 RequestDelegate,是否很眼熟,没错,管道处理!因此UseExceptionHandler 提供了一个简便的写法,可以让我们在ExceptionHandlerMiddleware 中又新建自定义的错误拦截管道来作为处理程序:
//in Configure()
app.UseExceptionHandler(appbuilder => appbuilder.Use(ExceptionHandlerDemo));
//该内容会在AspNetCore的管道返回结果至ExceptionHandlerMiddleware时,如果中间件捕获到了异常时调用
private async Task ExceptionHandlerDemo(HttpContext httpContext, Func next)
{
//该信息由ExceptionHandlerMiddleware中间件提供,里面包含了ExceptionHandlerMiddleware中间件捕获到的异常信息。 var exceptionDetails = httpContext.Features.Get<IExceptionHandlerFeature>(); var ex = exceptionDetails?.Error; if (ex != null) { httpContext.Response.ContentType = "application/problem+json"; var title = "An error occured: " + ex.Message; var details = ex.ToString(); var problem = new ProblemDetails { Status = 500, Title = title, Detail = details }; var stream = httpContext.Response.Body; await JsonSerializer.SerializeAsync(stream, problem); }
}
管道 VS 过滤器
那么上面两个方法有什么区别呢? 回答:拦截范围。
IExceptionFilter 作为MVC中间件之间的内容,它需要MVC在发现错误之后将错误信息提交给它处理,因此它的错误处理范围仅限于MVC中间件。所以,假如我们需要捕获MVC中间件之前的一些错误,其实是捕获不到的。 而对于ExceptionHandlerMiddleware中间件来说就很简单了,它作为第一个中间件,凡是在它之后的所有错误它都能够捕获得到。
那么这么看来是否IExceptionFilter就毫无用武之地了呢? 非也,假如您想在MVC发生异常时快速捕获和处理,使用过滤器其实是您不错得选择,如果您仅仅关心控制器之间的异常,那么过滤器也是很好的选择。
还记得刚开始我们在过滤器中说过的这一行代码吗:context.ExceptionHandled = true;。如果在IExceptionFilter中将异常标记为已经处理之后,则第一道异常处理中间件就认为没有错误了,不会进入到处理逻辑中。所以,如果咱们不把该属性改为 true,很有可能出现拦截结果被覆盖的情况。
低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。
持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。
转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。
- 上一篇
Java必学MySQL数据库应用场景
Java必学之MySQL数据库应用场景,在当前的后台开发中,MySQL应用非常普遍,企业在选拔Java人才时也会考察求职者诸如性能优化、高可用性、备份、集群、负载均衡、读写分离等问题。想要成为高薪Java工程师,一定要熟练掌握MySQL,接下来好程序员Java教程小编就给大家分享MySQL数据库应用场景知识。1、单Master单Master的情况是普遍存在的,对于很多个人站点、初创公司、小型内部系统,考虑到成本、更新频率、系统重要性等问题,系统只依赖一个单例数据库提供服务,基本上已经满足需求。单Master场景下应该关注四点:数据备份(冷备、热备、温备)、数据还原、备份监控、数据文件远程存储。2、一主一从考虑一主一从的多数初衷是系统性能和系统高可用性问题,除了单Master场景中的备份工作需要做好以外,还有性能优化、读写分离、负载均衡三项重点工作需要考虑。3、一主n从一旦开始考虑一主多从的服务器架构,则证明你的系统对可用性、一致性、性能中一种或者多种的要求比较高。好多系统在开始搭建的时候都会往这个方向看齐,这样“看起来”系统会健壮很多。一主n从关注的重点是:数据一致性、脑裂、雪崩、连接...
- 下一篇
阿里云服务网格ASM公测来袭系列之五:部署应用到ASM的数据面集群中
前面的系列文章中已经提到Istio社区网站提供了一个适合于初学者的Bookinfo示例,通过这个示例可以很快了解它的一些基础概念和能力。同样地,在阿里云服务网格ASM产品中也提供了一个如何部署该Bookinfo示例的快速入门。本文介绍如何将一个应用示例部署到服务网格ASM 实例中的数据面集群中。 前提条件 已创建至少一个 ASM 实例,并已添加至少一个 ACK 集群到该实例中。 已通过 Kubernetes 命令行客户端 kubectl 连接到 ASM 实例中新添加的 ACK 集群,详情参见通过 kubectl 连接 Kubernetes 集群。 本文中的应用示例需要对外暴露访问,因此需要提前为集群添加入口网关。 背景信息 本文所使用应用示例是一个名为 Bookinfo 的书评应用。微服务架构如下图所示: 该应用由以下 4 个微服务构成: Pro
相关文章
文章评论
共有0条评论来说两句吧...
文章二维码
点击排行
推荐阅读
最新文章
- Hadoop3单机部署,实现最简伪集群
- CentOS8编译安装MySQL8.0.19
- CentOS7编译安装Cmake3.16.3,解决mysql等软件编译问题
- Windows10,CentOS7,CentOS8安装MongoDB4.0.16
- SpringBoot2整合MyBatis,连接MySql数据库做增删改查操作
- SpringBoot2整合Thymeleaf,官方推荐html解决方案
- Eclipse初始化配置,告别卡顿、闪退、编译时间过长
- Springboot2将连接池hikari替换为druid,体验最强大的数据库连接池
- Mario游戏-低调大师作品
- CentOS6,CentOS7官方镜像安装Oracle11G