为什么异常处理选择中间件?

传统的ASP.NET可以采用异常过滤器的方式处理异常,在ASP.NET CORE中,是以多个中间件连接而成的管道形式处理请求的,不过常用的五大过滤器得以保留,同样可以采用异常过滤器处理异常,但是异常过滤器不能处理MVC中间件以外的异常,为了全局统一考虑,采用中间件处理异常更为合适

为什么选择自定义异常中间件?

先来看看ASP.NET CORE 内置的三个异常处理中间件 DeveloperExceptionPageMiddleware , ExceptionHandlerMiddleware, StatusCodePagesMiddleware

1.DeveloperExceptionPageMiddleware 
 能给出详细的请求/返回/错误信息,因为包含敏感信息,所以仅适合开发环境

2.ExceptionHandlerMiddleware  (蒋神博客 http://www.cnblogs.com/artech/p/error-handling-in-asp-net-core-3.html)

仅处理500错误

3.StatusCodePagesMiddleware  (蒋神博客 http://www.cnblogs.com/artech/p/error-handling-in-asp-net-core-4.html)

能处理400-599之间的错误,但需要Response中不能包含内容(ContentLength=0 && ContentType=null,经实验不能响应mvc里未捕获异常)

由于ExceptionHandlerMiddleware和StatusCodePagesMiddleware的各自的限制条件,两者需要搭配使用。相比之下自定义中间件更加灵活,既能对各种错误状态进行统一处理,也能按照配置决定处理方式。

CustomExceptionMiddleWare

首先声明异常中间件的配置类

     /// <summary>
/// 异常中间件配置对象
/// </summary>
public class CustomExceptionMiddleWareOption
{
public CustomExceptionMiddleWareOption(
CustomExceptionHandleType handleType = CustomExceptionHandleType.JsonHandle,
IList<PathString> jsonHandleUrlKeys = null,
string errorHandingPath = "")
{
HandleType = handleType;
JsonHandleUrlKeys = jsonHandleUrlKeys;
ErrorHandingPath = errorHandingPath;
} /// <summary>
/// 异常处理方式
/// </summary>
public CustomExceptionHandleType HandleType { get; set; } /// <summary>
/// Json处理方式的Url关键字
/// <para>仅HandleType=Both时生效</para>
/// </summary>
public IList<PathString> JsonHandleUrlKeys { get; set; } /// <summary>
/// 错误跳转页面
/// </summary>
public PathString ErrorHandingPath { get; set; }
} /// <summary>
/// 错误处理方式
/// </summary>
public enum CustomExceptionHandleType
{
JsonHandle = , //Json形式处理
PageHandle = , //跳转网页处理
Both = //根据Url关键字自动处理
}

声明异常中间件的成员

        /// <summary>
/// 管道请求委托
/// </summary>
private RequestDelegate _next; /// <summary>
/// 配置对象
/// </summary>
private CustomExceptionMiddleWareOption _option; /// <summary>
/// 需要处理的状态码字典
/// </summary>
private IDictionary<int, string> exceptionStatusCodeDic; public CustomExceptionMiddleWare(RequestDelegate next, CustomExceptionMiddleWareOption option)
{
_next = next;
_option = option;
exceptionStatusCodeDic = new Dictionary<int, string>
{
{ , "未授权的请求" },
{ , "找不到该页面" },
{ , "访问被拒绝" },
{ , "服务器发生意外的错误" }
//其余状态自行扩展
};
}

异常中间件主要逻辑

         public async Task Invoke(HttpContext context)
{
Exception exception = null;
try
{
await _next(context); //调用管道执行下一个中间件
}
catch (Exception ex)
{
context.Response.Clear();
context.Response.StatusCode = ; //发生未捕获的异常,手动设置状态码
exception = ex;
}
finally
{
if (exceptionStatusCodeDic.ContainsKey(context.Response.StatusCode) &&
!context.Items.ContainsKey("ExceptionHandled")) //预处理标记
{
var errorMsg = string.Empty;
if (context.Response.StatusCode == && exception != null)
{
errorMsg = $"{exceptionStatusCodeDic[context.Response.StatusCode]}\r\n{(exception.InnerException != null ? exception.InnerException.Message : exception.Message)}";
}
else
{
errorMsg = exceptionStatusCodeDic[context.Response.StatusCode];
}
exception = new Exception(errorMsg);
} if (exception != null)
{
var handleType = _option.HandleType;
if (handleType == CustomExceptionHandleType.Both) //根据Url关键字决定异常处理方式
{
var requestPath = context.Request.Path;
handleType = _option.JsonHandleUrlKeys != null && _option.JsonHandleUrlKeys.Count(
k => requestPath.StartsWithSegments(k, StringComparison.CurrentCultureIgnoreCase)) > ?
CustomExceptionHandleType.JsonHandle :
CustomExceptionHandleType.PageHandle;
} if (handleType == CustomExceptionHandleType.JsonHandle)
await JsonHandle(context, exception);
else
await PageHandle(context, exception, _option.ErrorHandingPath);
}
}
} /// <summary>
/// 统一格式响应类
/// </summary>
/// <param name="ex"></param>
/// <returns></returns>
private ApiResponse GetApiResponse(Exception ex)
{
return new ApiResponse() { IsSuccess = false, Message = ex.Message };
} /// <summary>
/// 处理方式:返回Json格式
/// </summary>
/// <param name="context"></param>
/// <param name="ex"></param>
/// <returns></returns>
private async Task JsonHandle(HttpContext context, Exception ex)
{
var apiResponse = GetApiResponse(ex);
var serialzeStr = JsonConvert.SerializeObject(apiResponse);
context.Response.ContentType = "application/json";
await context.Response.WriteAsync(serialzeStr, Encoding.UTF8);
} /// <summary>
/// 处理方式:跳转网页
/// </summary>
/// <param name="context"></param>
/// <param name="ex"></param>
/// <param name="path"></param>
/// <returns></returns>
private async Task PageHandle(HttpContext context, Exception ex, PathString path)
{
context.Items.Add("Exception", ex);
var originPath = context.Request.Path;
context.Request.Path = path; //设置请求页面为错误跳转页面
try
{
await _next(context);
}
catch { }
finally
{
context.Request.Path = originPath; //恢复原始请求页面
}
}

使用扩展类进行中间件注册

  public static class CustomExceptionMiddleWareExtensions
{ public static IApplicationBuilder UseCustomException(this IApplicationBuilder app, CustomExceptionMiddleWareOption option)
{
return app.UseMiddleware<CustomExceptionMiddleWare>(option);
}
}

在Startup.cs的Configuref方法中注册异常中间件

   app.UseCustomException(new CustomExceptionMiddleWareOption(
handleType: CustomExceptionHandleType.Both, //根据url关键字决定处理方式
jsonHandleUrlKeys: new PathString[] { "/api" },
errorHandingPath: "/home/error"));

接下来我们来进行测试,首先模拟一个将会进行页面跳转的未经捕获的异常

访问/home/about的结果

访问/home/test的结果 (该地址不存在)

OK异常跳转页面的方式测试完成,接下来我们测试返回统一格式(json)的异常处理,同样先模拟一个未经捕获的异常

访问/api/token/gettesterror的结果

访问/api/token/test的结果 (该地址不存在)

访问/api/token/getvalue的结果 (该接口需要身份验证)

测试完成,页面跳转和统一格式返回都没有问题,自定义异常中间件已按预期工作

需要注意的是,自定义中间件会响应每个HTTP请求,所以处理逻辑一定要精简,防止发生不必要的性能问题

ASP.NET CORE 学习之自定义异常处理的更多相关文章

  1. WebAPI调用笔记 ASP.NET CORE 学习之自定义异常处理 MySQL数据库查询优化建议 .NET操作XML文件之泛型集合的序列化与反序列化 Asp.Net Core 轻松学-多线程之Task快速上手 Asp.Net Core 轻松学-多线程之Task(补充)

    WebAPI调用笔记   前言 即时通信项目中初次调用OA接口遇到了一些问题,因为本人从业后几乎一直做CS端项目,一个简单的WebAPI调用居然浪费了不少时间,特此记录. 接口描述 首先说明一下,基于 ...

  2. Asp.Net Core学习笔记:入门篇

    Asp.Net Core 学习 基于.Net Core 2.2版本的学习笔记. 常识 像Django那样自动检查代码更新,自动重载服务器(太方便了) dotnet watch run 托管设置 设置项 ...

  3. ASP.NET Core中显示自定义错误页面-增强版

    之前的博文 ASP.NET Core中显示自定义错误页面 中的方法是在项目中硬编码实现的,当有多个项目时,就会造成不同项目之间的重复代码,不可取. 在这篇博文中改用middleware实现,并且放在独 ...

  4. ASP.NET Core学习系列

    .NET Core ASP.NET Core ASP.NET Core学习之一 入门简介 ASP.NET Core学习之二 菜鸟踩坑 ASP.NET Core学习之三 NLog日志 ASP.NET C ...

  5. ASP.NET Core学习指导

    ASP.NET Core 学习指导 "工欲善其事必先利其器".我们在做事情之前,总应该做好充分的准备,熟悉自己的工具.就像玩游戏有一些最低配置一样,学习一个新的框架,也需要有一些基 ...

  6. asp.net core 3.1 自定义中间件实现jwt token认证

    asp.net core 3.1 自定义中间件实现jwt token认证 话不多讲,也不知道咋讲!直接上代码 认证信息承载对象[user] /// <summary> /// 认证用户信息 ...

  7. asp.net core 实现支持自定义 Content-Type

    asp.net core 实现支持自定义 Content-Type Intro 我们最近有一个原本是内网的服务要上公网,在公网上有一层 Cloudflare 作为网站的公网流量提供者,CloudFla ...

  8. ASP.NET Core学习之一 入门简介

    一.入门简介 在学习之前,要先了解ASP.NET Core是什么?为什么?很多人学习新技术功利心很重,恨不得立马就学会了. 其实,那样做很不好,马马虎虎,联系过程中又花费非常多的时间去解决所遇到的“问 ...

  9. Asp.net Core学习笔记

    之前记在github上的,现在搬运过来 变化还是很大的,感觉和Nodejs有点类似,比如中间件的使用 ,努力学习ing... 优点 不依赖IIS 开源和跨平台 中间件支持 性能优化 无所不在的依赖注入 ...

随机推荐

  1. TPC-E在populate测试Database时需要注意的一些事项

    第一,  安装时不要使用named instance, 默认的instance就好. 否则会报连不上Database. 第二, TPC-E工具文件夹的完整路径中不可以有空格, 否则会在generate ...

  2. android系统访问自己的tomcat服务器下的项目不能访问的原因

    今天做android的一个下载功能,用自己机子上的tomcat做服务器,在tomcat上下载东西,可是android系统老是提示错误说不能连接到我的tomcat,可是我明明启动了tomcat服务啊,而 ...

  3. Faster\Slower 快慢指针的应用

    leetcode很多题目都是利用快慢指针来解决题目,下面具体讲解下快慢指针. 概念: 快指针在每一步走的步长要比慢指针一步走的步长要多.快指针通常的步速是慢指针的2倍.在循环中的指针移动通常为:fas ...

  4. 【WCF】HTTP 无法注册 URL 进程,不具有此命名空间的访问权限

    背景   如题,在运行WCF宿主主机时,出现了问题.   捕获异常为:HTTP 无法注册 URL http://+:8000/WCF/.进程不具有此命名空间的访问权限(有关详细信息,请参见 http: ...

  5. ASM下裸设备的路径更改是否会影响数据库的执行

    通过asm来存储数据库文件,在linux下能够通过asmlib的方式来管理块设备,也能够直接使用裸设备来建立asm磁盘.在asmlib方式下,磁盘设备启动顺序和名称的改变不会影响到asm的使用.但假设 ...

  6. 使用Gnupg对Linux系统中的文件进行加密

    GnuPG(GNU Privacy Guard或GPG)是一个以GNU通用公共许可证释出的开放源码用于加密或签名的软件,可用来取代PGP.大多数gpg软件仅支持命令行方式,一般人较难掌握.由于gpg软 ...

  7. Aliasing input/output properties

    angular @input alias别名的使用. https://angular.io/guide/template-syntax#aliasing-io https://stackoverflo ...

  8. [Algorithm] Write your own Math.pow function in Javascript, using Recursive approach

    /**@abstract * Write your own Math.pow(a int, b int) function * */ function pow (a, b) { let result ...

  9. 在服务器端判断request来自Ajax请求(异步)还是传统请求(同步)

    两种请求在请求的Header不同,Ajax 异步请求比传统的同步请求多了一个头参数 1. 传统同步请求参数 accept text/html,application/xhtml+xml,applica ...

  10. ssh2的application.xml配置文件配置详解

    ssh2的application.xml配置文件配置详解   1.导入其他的配置文件.在ssh项目中可以导入其他的配置文件,导入的格式为: <import resource="clas ...