接下来介绍使用代理方法的方式,也就是说把 ErrorController 整段逻辑直接定义在注册的地方,使用一个匿名委托来处理,这里的逻辑与之前的逻辑是相同的

app.UseExceptionHandler(errApp =>
{
errApp.Run(async context =>
{
// 在 Features 里面获取异常
var exceptionHandlerPathFeature = context.Features.Get<IExceptionHandlerPathFeature>();
// 识别异常是否为 IKnownException
IKnownException knownException = exceptionHandlerPathFeature.Error as IKnownException;
if (knownException == null)
{
// 如果不是则记录并且把错误的响应码响应成 Http 500
var logger = context.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
logger.LogError(exceptionHandlerPathFeature.Error, exceptionHandlerPathFeature.Error.Message);
knownException = KnownException.Unknown;
context.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
else
{
// 如果捕获到的是一个业务逻辑的异常,Http 响应码应该给是 200
knownException = KnownException.FromKnownException(knownException);
context.Response.StatusCode = StatusCodes.Status200OK;
}
// 然后再把响应信息通过 json 的方式输出出去
var jsonOptions = context.RequestServices.GetService<IOptions<JsonOptions>>();
context.Response.ContentType = "application/json; charset=utf-8";
await context.Response.WriteAsync(System.Text.Json.JsonSerializer.Serialize(knownException, jsonOptions.Value.JsonSerializerOptions));
});
});

为什么对于未知的异常要输出 Http 500,而对于业务逻辑的异常,建议输出 Http 200?

因为监控系统实际上会对 Http 的响应码进行识别,当监控系统识别到 Http 响应是 500 的比例比较高的情况下,会认为系统的可用性有问题,这个时候告警系统就会发出警告

对于已知的业务逻辑的这种正常的识别的话,用正常的 Http 200 来处理是一个正常的行为,这样就可以让监控系统更好的工作,正确的识别出系统的一些未知的错误信息,错误的告警,让告警系统更加的灵敏,也避免了业务逻辑的异常干扰告警系统

接下来看一下第三种,通过异常过滤器的方式

这种方式实际上是作用在 MVC 的整个框架的体系下面的,它并不是在中间件的最早期发生作用的,它是在 MVC 的整个生命周期里面发生作用,也就是说它只能工作在 MVC Web API 的请求周期里面

首先自定义一个 MyExceptionFilter

namespace ExceptionDemo.Exceptions
{
public class MyExceptionFilter : IExceptionFilter
{
public void OnException(ExceptionContext context)
{
IKnownException knownException = context.Exception as IKnownException;
if (knownException == null)
{
var logger = context.HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
logger.LogError(context.Exception, context.Exception.Message);
knownException = KnownException.Unknown;
context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
else
{
knownException = KnownException.FromKnownException(knownException);
context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
}
context.Result = new JsonResult(knownException)
{
ContentType = "application/json; charset=utf-8"
};
}
}
}

处理逻辑与之前的相同

接着注册 Filters

services.AddMvc(mvcOptions =>
{
mvcOptions.Filters.Add<MyExceptionFilter>();
}).AddJsonOptions(jsonoptions =>
{
jsonoptions.JsonSerializerOptions.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
});

启动程序,输出如下:

{"message":"未知错误","errorCode":9999,"errorData":null}

输出与之前的一致,因为这是在 Controller 里面输出了错误

如果在 MVC 的中间件之前输出错误的话,它是没办法处理的

这个场景一般情况下是指需要对 Controller 进行特殊的异常处理,而对于中间件整体来讲的话,又要用另一种特殊的逻辑来处理的时候,可以用 ExceptionFilter 的方式处理

这种方式还可以通过 Attribute 的方式

自定义一个 MyExceptionFilterAttribute

namespace ExceptionDemo.Exceptions
{
public class MyExceptionFilterAttribute : ExceptionFilterAttribute
{
public override void OnException(ExceptionContext context)
{
IKnownException knownException = context.Exception as IKnownException;
if (knownException == null)
{
var logger = context.HttpContext.RequestServices.GetService<ILogger<MyExceptionFilterAttribute>>();
logger.LogError(context.Exception, context.Exception.Message);
knownException = KnownException.Unknown;
context.HttpContext.Response.StatusCode = StatusCodes.Status500InternalServerError;
}
else
{
knownException = KnownException.FromKnownException(knownException);
context.HttpContext.Response.StatusCode = StatusCodes.Status200OK;
}
context.Result = new JsonResult(knownException)
{
ContentType = "application/json; charset=utf-8"
};
}
}
}

在 Controller 上面标注 MyExceptionFilter

[MyExceptionFilter]
public class WeatherForecastController : ControllerBase

启动运行之后效果相同

这两种方式的效果是对等的,区别在于说可以更细粒度的对异常处理进行控制,可以指定部分的 Controller 或者 Exception,来决定我们的异常处理,也可以在全局注册 ExceptionFilter

当然因为 ExceptionFilterAttribute 也实现了 IExceptionFilter,所以它也可以注册到全局,也可以把它当作全局异常处理的过滤器来使用,Controller 上面也就不需要标记了

注册 Filters

services.AddMvc(mvcOptions =>
{
//mvcOptions.Filters.Add<MyExceptionFilter>();
mvcOptions.Filters.Add<MyExceptionFilterAttribute>();
}).AddJsonOptions(jsonoptions =>
{
jsonoptions.JsonSerializerOptions.Encoder = System.Text.Encodings.Web.JavaScriptEncoder.UnsafeRelaxedJsonEscaping;
});

在 Controller 上面取消标注 MyExceptionFilter

//[MyExceptionFilter]
public class WeatherForecastController : ControllerBase

启动程序,输出结果一致

这个场景对于我们定义一些 API,然后对 API 进行定义我们的异常处理的约定是很有帮助的

总结一下

首先我们需要定义特定的异常类或者接口,我们可以定义抽象类,也可以用接口的方式,例子中是通过接口的方式表示业务逻辑的异常

对于业务逻辑的异常,实际上需要定义全局的错误码

对于未知的异常,应该输出特定的输出信息和错误码,然后记录完整的日志,我们不应该把系统内部的一些比如说异常堆栈这些信息输出给用户

对于已知的业务逻辑的异常,用 Http 200 的方式,对于未知的异常,用 Http 500 的方式,这样可以让监控系统更好的工作

另外一个建议就是尽量记录所有的异常的详细信息,以供后续对日志进行分析,也供监控系统做一些特定的监控警告

GitHub源码链接:

https://github.com/MingsonZheng/DotNetCoreDevelopmentActualCombat/tree/main/ExceptionDemo

本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。

欢迎转载、使用、重新发布,但务必保留文章署名 郑子铭 (包含链接: http://www.cnblogs.com/MingsonZheng/ ),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。

如有任何疑问,请与我联系 (MingsonZheng@outlook.com) 。

.NET Core开发实战(第22课:异常处理中间件:区分真异常与逻辑异常)--学习笔记(下)的更多相关文章

  1. 2月送书福利:ASP.NET Core开发实战

    大家都知道我有一个公众号“恰童鞋骚年”,在公众号2020年第一天发布的推文<2020年,请让我重新介绍我自己>中,我曾说到我会在2020年中每个月为所有关注“恰童鞋骚年”公众号的童鞋们送一 ...

  2. [ASP.NET Core开发实战]开篇词

    前言 本系列课程文章主要是学习官方文档,再输出自己学习心得,希望对你有所帮助. 课程大纲 本系列课程主要分为三个部分:基础篇.实战篇和部署篇. 希望通过本系列课程,能让大家初步掌握使用ASP.NET ...

  3. 潭州课堂25班:Ph201805201 django框架 第十二课 自定义中间件,上下文处理,admin后台 (课堂笔记)

    中间件 在项目主目录下的配置文件 在项目主目录下创建文件 写个自定义异常处理 方法1 要让其生效,要在主目录下,的中间件中进行注册 主目录下.该文件名.类名 在进入视图函数之前进行判断,  给 req ...

  4. .NET Core开发实战(第11课:文件配置提供程序)--学习笔记

    11 | 文件配置提供程序:自由选择配置的格式 文件配置提供程序 Microsoft.Extensions.Configuration.Ini Microsoft.Extensions.Configu ...

  5. 2、SpringBoot接口Http协议开发实战8节课(7-8)

    7.SpringBoot2.x文件上传实战 简介:讲解HTML页面文件上传和后端处理实战 1.讲解springboot文件上传 MultipartFile file,源自SpringMVC 1)静态页 ...

  6. 2、SpringBoot接口Http协议开发实战8节课(1-6)

    1.SpringBoot2.xHTTP请求配置讲解 简介:SpringBoot2.xHTTP请求注解讲解和简化注解配置技巧 1.@RestController and @RequestMapping是 ...

  7. [ASP.NET Core开发实战]基础篇03 中间件

    什么是中间件 中间件是一种装配到应用管道,以处理请求和响应的组件.每个中间件: 选择是否将请求传递到管道中的下一个中间件. 可在管道中的下一个中间件前后执行. ASP.NET Core请求管道包含一系 ...

  8. [ASP.NET Core开发实战]基础篇02 依赖注入

    ASP.NET Core的底层机制之一是依赖注入(DI)设计模式,因此要好好掌握依赖注入的用法. 什么是依赖注入 我们看一下下面的例子: public class MyDependency { pub ...

  9. [ASP.NET Core开发实战]基础篇01 Startup

    Startup,顾名思义,就是启动类,用于配置ASP.NET Core应用的服务和请求管道. Startup有两个主要作用: 通过ConfigureServices方法配置应用的服务.服务是一个提供应 ...

  10. [ASP.NET Core开发实战]基础篇06 配置

    配置,是应用程序很重要的组成部分,常常用于提供信息,像第三方应用登录钥匙.上传格式与大小限制等等. ASP.NET Core提供一系列配置提供程序读取配置文件或配置项信息. ASP.NET Core项 ...

随机推荐

  1. excel如何自动获取本周工作日的日期显示作为标题(周一至周五)

    一.背景: 每周发周报的标题格式为:本周一到本周五的日期内容,如下:但是每隔一周发送的时候需要改一下周报标题里面的日期,比较麻烦目前的需求是通过函数自动化生成,根据当前的日期去定位出本周一的日期以及本 ...

  2. Postman 接口测试配置 Pre-request Script

    本文为博主原创,转载请注明出处:  Pre-request Script 为Postman预置脚本,用于在postman 发送请求之前执行,封装计算或获取某些请求参数. 1. postman 脚本提供 ...

  3. mysql-数值函数-取整-保留小数位-求余数

  4. [转帖]linux将大目录等分切割成多个小目录

    https://www.jianshu.com/p/6f9e6743a1dc 需求:有一个目录存放了数十万个文件,现在需要将这个目录上传,如果整个目录上传,中间因为某些故障断开连接了,可能又要从头开始 ...

  5. [转帖]Linux文件系统的几个性能测试软件小结

    https://developer.aliyun.com/article/297631#:~:text=Linux%E6%96%87%E4%BB%B6%E7%B3%BB%E7%BB%9F%E7%9A% ...

  6. [转帖]jmeter_采样器sampler简介

    1.取样器介绍 取样器是用来模拟用户操作的,向服务器发送请求以及接收服务器的响应数据. 取样器是在线程组内部的元件,也就是说取样器只能在线程组中添加. 取样器(Sampler)是性能测试中向服务器发送 ...

  7. [转帖]Linux查看raid1和raid10分别由哪些盘组成,在哪个槽位

    查找有问题的盘 MegaCli64 -PDList -aALL |grep "Firmware state" 6个盘,2个坏了 查看raid级别和硬盘的状态 MegaCli64 - ...

  8. [转帖]NGINX 局限太多,Cloudflare 最终放弃它并用 Rust 自研了全新替代品

    https://www.infoq.cn/news/s2fa603MsEENsCmibTYI 长期以来,NGINX 可以说是网站安全和托管服务提供商 Cloudflare 的核心,是其所使用的基础软件 ...

  9. Find 查找并且展示最近24小时内创建的文件信息

    1. 命令为: find /gscloud/tools/patchinstall/patchfiles/ -maxdepth 1 -mtime 1 |cut -c40- >/deploy/pat ...

  10. 【JS 逆向百例】猿人学系列 web 比赛第五题:js 混淆 - 乱码增强,详细剖析

    逆向目标 猿人学 - 反混淆刷题平台 Web 第五题:js 混淆,乱码增强 目标:抓取全部 5 页直播间热度,计算前 5 名直播间热度的加和 主页:https://match.yuanrenxue.c ...