ASP.NET Core 中间件自定义全局异常处理
目录
- 背景
- ASP.NET Core过滤器(Filter)
- ASP.NET Core 中间件(Middleware)
- 自定义全局异常处理
- .Net Core中使用ExceptionFilter
- .Net Core中使用中间件
- 总结
- 参考
背景
作为开发者,你兴高采烈地完成了新系统的功能开发。并且顺利经过验收,系统如期上线,皆大欢喜。
但是,有些bug就是在生产环境如期而至了。半夜梦酣之时,你被运维童鞋的电话惊醒了,系统不能正常运行了。接下来,他打包了一堆日志文件给你...
笔者有幸做过几年运维自动化系统,深知产品的每一次大跌代上线都是一场很多IT人的噩梦。更甚者,开发和运维人员有时候因为定位一个线上问题,花了一个通宵或者甚至版本回退。
干了多年开发越来越觉得,异常处理和定位的能力反映出开发者硬核能力。如果开发人员能够在对系统中异常进行捕获,然后记录日志,并对日志进行划分等级,然后通过邮件或者短信等提醒,是不是能够做到提前预判呢。
在 asp.net core中全局异常处理,这里介绍两种不同的处理方式:过滤器捕获和中间件过滤。
过滤器
通过使用 ASP.NET Core 中的筛选器,可在请求处理管道中的特定阶段之前或之后运行代码。
内置过滤器处理任务,例如:
- 授权(防止用户访问未获授权的资源)。
- 响应缓存(对请求管道进行短路出路,以便返回缓存的响应)。
可以创建自定义过滤器,用于处理横切关注点。 横切关注点的示例包括错误处理、缓存、配置、授权和日志记录。 过滤器可以避免复制代码。 例如,错误处理异常过滤器可以合并错误处理。
过滤器的工作原理
过滤器在 ASP.NET Core 操作调用管道(有时称过滤器管道)内运行。 过滤器管道在 ASP.NET Core 选择了要执行的操作之后运行。

过滤器类型
熟悉.NET MVC框架的同学应该知道,MVC也提供了5大过滤器供我们用来处理请求前后需要执行的代码。分别是授权过滤器(AuthenticationFilter),资源过滤器(resource-filters),操作过滤器(ActionFilter),异常过滤器(ExceptionFilter),结果过滤器(ResultFilter)。
每种过滤选器类型都过滤器管道中的不同阶段执行:
授权过滤器最先运行,用于确定是否已针对请求为用户授权。 如果请求未获授权,授权过滤器可以让管道短路。
资源过滤器:
- 授权后运行。
- OnResourceExecuting 在过滤器管道的其余阶段之前运行代码。 例如,OnResourceExecuting 在模型绑定之前运行代码。
- OnResourceExecuted 在管道的其余阶段完成之后运行代码。
操作过滤器:
- 在调用操作方法之前和之后立即运行代码。
- 可以更改传递到操作中的参数。
- 可以更改从操作返回的结果。
- 不可在 Razor Pages 中使用。
异常过滤器在向响应正文写入任何内容之前,对未经处理的异常应用全局策略。
结果过滤器在执行操作结果之前和之后立即运行代码。 仅当操作方法成功执行时,它们才会运行。 对于必须围绕视图或格式化程序的执行的逻辑,它们很有用。
下图展示过滤器类型在筛选器管道中的交互方式。

过滤器使用
在.net core 中,一般是在StartUp.cs的ConfigureServices方法中注册
// 将异常过滤器注入到容器中
services.AddScoped<GlobalExceptionFilter>();
中间件
中间件(Middleware)的作用
ASP.NETCore应用基于一系列中间件构建。中间件是排列到管道中的处理程序,用于处理请求和响应。 在 Web 窗体应用程序中,HTTP 处理程序和模块解决了类似的问题。 在 ASP.NET Core 中,模块、处理程序、 Global.asax.cs和应用程序生命周期替换为中间件。
ASP.NET Core 请求管道包含一系列请求委托,依次调用。 下图演示了这一概念。 沿黑色箭头执行。

可以看到,每一个中间件都都可以在请求之前和之后进行操作。请求处理完成之后传递给下一个请求。
中间件的运行方式
默认情况下,中间件的执行顺序根据Startup.cs中,Configure方法中注册的先后顺序执行。一般通过aApp.UseMiddleware<>()的方式注册中间件。
// ExceptionMiddleware 加入管道
app.UseMiddleware<ExceptionMiddleware>();
使用ExceptionFilter
前面提到,过滤器可以处理错误异常。这里可以实践一把。
新建一个.NET Core MVC控制器(.net WebAPI也类似)。
我在Test/Index Action方法中故意制造一个异常(我们知道在被除数不能为0).
public IActionResult Index()
{
int a = 0, b = 5;
var result = b/a;
}
在Visual Studio中调试报错了

我们深知,异常这样报错很不友好,于是我们用了万能的try-catch
public IActionResult Index()
{
try
{
int a = 0, b = 5;
var result = b / a;
} catch (Exception)
{
throw new ArgumentException("被除数不能为0", "a");
}
}
这样异常提示确实友好了,并且我们拦截了异常,甚至可以将异常记录到日志中。

但是每个方法都这样加会不会觉得很烦?有没有想过一劳永逸的办法。从架构层面应该这样思考。
在传统的 Asp.Net MVC 应用程序中,我们一般都使用服务过滤的方式去捕获和处理异常,这种方式 非常常见,而且可用性来说,体验也不错,幸运的是 Asp.Net Core也完整的支持该方式。 新建一个全局异常过滤器GlobalExceptionFilter.cs,继承自IExceptionFilter。
public class GlobalExceptionFilter:Attribute, IExceptionFilter
{
private readonly IHostingEnvironment _hostingEnvironment;
private readonly IModelMetadataProvider _modelMetadataProvider;
public GlobalExceptionFilter(
IHostingEnvironment hostingEnvironment,
IModelMetadataProvider modelMetadataProvider)
{
_hostingEnvironment = hostingEnvironment;
_modelMetadataProvider = modelMetadataProvider;
}
/// <summary>
/// 发生异常进入
/// </summary>
/// <param name="context"></param>
public async void OnException(ExceptionContext context)
{
ContentResult result = new ContentResult
{
StatusCode = 500,
ContentType = "text/json;charset=utf-8;"
};
if (_hostingEnvironment.IsDevelopment())
{
var json = new { message = context.Exception.Message };
result.Content = JsonConvert.SerializeObject(json);
}
else
{
result.Content = "抱歉,出错了";
}
context.Result = result;
context.ExceptionHandled = true;
}
}
我们在startup.cs中进行中注入
// 将异常过滤器注入到容器中
services.AddScoped<GlobalExceptionFilter>();
然后在需要的控制器上加上特性**ServiceFilter(typeof(GlobalExceptionFilter))]
[ServiceFilter(typeof(GlobalExceptionFilter))]
public class TestController : Controller
启动程序,错误提示,页面只会显示单纯错误信息。
{"message":"Attempted to divide by zero."}
Net Core中使用中间件方式
首先,创建一个中间件ExceptionMiddleware
public class ExceptionMiddleware
{
private readonly RequestDelegate next;
private IHostingEnvironment environment;
public ExceptionMiddleware(RequestDelegate next,IHostingEnvironment environment)
{
this.next = next;
this.environment = environment;
}
public async Task Invoke(HttpContext context)
{
try
{
await next.Invoke(context);
var features = context.Features;
}
catch (Exception e)
{
await HandleException(context, e);
}
}
private async Task HandleException(HttpContext context, Exception e)
{
context.Response.StatusCode = 500;
context.Response.ContentType = "text/json;charset=utf-8;";
string error = "";
if (environment.IsDevelopment())
{
var json = new { message = e.Message};
error = JsonConvert.SerializeObject(json);
}
else
error = "抱歉,出错了";
await context.Response.WriteAsync(error);
}
}
创建 HandleException(HttpContext context, Exception e) 处理异常,判断是 Development 环境下,输出详细的错误信息,非 Development 环境仅提示调用者“抱歉,出错了”,同时使用 NLog 组件将日志写入硬盘;同样,在 Startup.cs 中将 ExceptionMiddleware 加入管道中
//ExceptionMiddleware 加入管道
app.UseMiddleware<ExceptionMiddleware>();
启动调试,结果如下
{"message":"Attempted to divide by zero."}
统一封装冷异常处理方式和消息格式,对前端也很友好。
总结
通过依赖注入和管道中间件两种不同的全局捕获异常处理。实际项目中,也是应当区分不同的业务场景,输出不同的日志信息,不管是从安全或者是用户体验友好性上面来说,都是非常值得推荐的方式,全局异常捕获处理,完全和业务剥离。
从运维的角度看,将异常处理的日志进行统一采集和分类,便于接入ELK,或者第三方日志系统。方便检测日志,从而监测系统健康状况。
参考

ASP.NET Core 中间件自定义全局异常处理的更多相关文章
- ASP.NET Core 中间件 自定义全局异常中间件以及 MVC异常过滤器作用
中间件是一种装配到应用管道以处理请求和响应的软件. 每个组件: 选择是否将请求传递到管道中的下一个组件. 可在管道中的下一个组件前后执行工作. 请求委托用于生成请求管道. 请求委托处理每个 HTTP ...
- ASP.NET Core 中间件的使用(三):全局异常处理机制
前言 我们经常听到"秒修复秒上线",觉得很厉害的样子. 其实不然,这只是一个调侃而已,出现问题的方式很多(逻辑漏洞.代码异常.操作方式不正确等). 我们今天来说代码异常问题怎么快速 ...
- 如何一秒钟从头构建一个 ASP.NET Core 中间件
前言 其实地上本没有路,走的人多了,也便成了路. -- 鲁迅 就像上面鲁迅说的那样,其实在我们开发中间件的过程中,微软并没有制定一些策略或者文档来约束你如何编写一个中间件程序, 但是其中却存在者一些最 ...
- (4)ASP.NET Core 中间件
1.前言 整个HTTP Request请求跟HTTP Response返回结果之间的处理流程是一个请求管道(request pipeline).而中间件(middleware)则是一种装配到请求管道以 ...
- ASP.NET Core 中间件Diagnostics使用
ASP.NET Core 中间件(Middleware)Diagnostics使用.对于中间件的介绍可以查看之前的文章ASP.NET Core 开发-中间件(Middleware). Diagnost ...
- [转]ASP.NET Core 中间件详解及项目实战
本文转自:http://www.cnblogs.com/savorboard/p/5586229.html 前言 在上篇文章主要介绍了DotNetCore项目状况,本篇文章是我们在开发自己的项目中实际 ...
- 如何在ASP.NET Core中自定义Azure Storage File Provider
文章标题:如何在ASP.NET Core中自定义Azure Storage File Provider 作者:Lamond Lu 地址:https://www.cnblogs.com/lwqlun/p ...
- ASP.NET Core 入门教程 9、ASP.NET Core 中间件(Middleware)入门
一.前言 1.本教程主要内容 ASP.NET Core 中间件介绍 通过自定义 ASP.NET Core 中间件实现请求验签 2.本教程环境信息 软件/环境 说明 操作系统 Windows 10 SD ...
- ASP.NET Core -中间件(Middleware)使用
ASP.NET Core开发,开发并使用中间件(Middleware). 中间件是被组装成一个应用程序管道来处理请求和响应的软件组件. 每个组件选择是否传递给管道中的下一个组件的请求,并能之前和下一组 ...
随机推荐
- 微信公众平台三种IP白名单场景及设置问题
在开发使用微信公众平台时,目前遇到有三处需要配置IP白名单. 1.微信公众平台,“获取access_token”接口新增IP白名单保护,官网:https://mp.weixin.qq.com/cgi- ...
- locate及find查找命令
在文件系统上查找符合条件的文件: 实现工具:locate,find locate: 依赖于事先构建好的索引库: 系统自动实现(周期性任务): 手动更新数 ...
- ios Alamofire网络插件的使用
pod 'Alamofire' import Alamofire let headers:HTTPHeaders = [ "aa":"bb" ] let par ...
- POJ3276 Face The Right Way 开关问题
①每个K从最左边进行考虑 ②f[i]=[i,i+k-1]是否进行反转:1代表是,0代表否 ③∑ (i)(i=i+1-K+1) f[j]=∑ (i-1)(i=i-K+1) f[j]+f[i]-f[i-K ...
- 关于vyos 防火墙配置
VyOS是一个基于Debian的网络操作系统,是Vyatta的社区fork.Vyatta是博通的企业级的产品,通过这套系统,能在x86平台提供路由,防火墙和×××的功能. 这个系统提供了和其他诸如Ci ...
- Amsterdam Distance
题目描述 Your friend from Manhattan is visiting you in Amsterdam. Because she can only stay for a short ...
- python学习笔记(3)数据类型-列表list
序列是Python中最基本的数据结构.序列中的每个元素都分配一个数字 - 它的位置,或索引,第一个索引是0,第二个索引是1,依此类推. Python有6个序列的内置类型,但最常见的是列表和元组. 序列 ...
- orcale 11g安装,创建表空间,用户,授权用户
一.卸载旧oracle 用Oracle自带的卸载程序不能从根本上卸载Oracle,从而为下次的安装留下隐患,那么怎么才能完全卸载Oracle呢?那就是直接注册表清除,步骤如下: 1. 开始->设 ...
- 吴裕雄--天生自然python学习笔记:Python3 面向对象
Python从设计之初就已经是一门面向对象的语言,正因为如此,在Python中创建一个类和对象是很容易的. 面向对象技术简介 类(Class): 用来描述具有相同的属性和方法的对象的集合.它定义了该集 ...
- spring配置ConcurrentMap实现缓存
spring本身内置了对Cache的支持,本次记录的是基于Java API的ConcurrentMap的CacheManager配置. 1.xml文件中增加命名空间 <beans xmlns=& ...