我们基于 Razor Class Library 实现了自定义错误页面的公用类库(详见之前的随笔),但是在实际使用时发现如果在 middleware 中发生了异常,则不能显示自定义错误页面,而是返回默认的 500 空白页面。

public static IApplicationBuilder UseCustomErrorPages(this IApplicationBuilder app)
{app.UseExceptionHandler("/errors/500");app.UseStatusCodePagesWithReExecute("/errors/{0}");return app;
}

自定义错误页面使用的是上面的配置,当发生异常时,会走路由 /errors/500 到达对应的自定义错误页面的 mvc action 。 如果是 mvc 中产生异常,能正常到达;但是当 middleware 中产生异常时,在去往自定义错误页面的途中,又途径异常 middleware ,从而让自定义错误页面也产生了异常。这就是自定义错误页面不能显示的原因。

问题的原因想明白了,接下来通过集成测试重现这个问题。

public class ErrorPageTests : IClassFixture<WebApplicationFactory<Startup>>
{  [Fact]public async Task ErrorPageForMiddleWareException(){var client = _factory.WithWebHostBuilder(builder =>{builder.ConfigureServices(services => services.AddMvc());builder.Configure(app =>{app.UseCustomErrorPages();app.Use(next => context => throw new Exception("Failed in middleware"));app.UseMvcWithDefaultRoute();                    });}).CreateClient();var response = await client.GetAsync("/");Assert.Equal(HttpStatusCode.InternalServerError, response.StatusCode);var content = await response.Content.ReadAsStringAsync();Assert.Contains($"请求失败:500", content);}
}

先写好测试的好处之一是在尝试解决方法时可以快速得到反馈,虽然写测试代码会花去更多时间,但仅这一点好处就得远大于失。

有了测试代码的保驾护航,这时就可以放心大胆的动手尝试解决问题了。

既然问题的根源是“在去往自定义错误页面的途中,又途径异常 middleware”,那我们只要抄近路绕过这些 middlewares ,直接奔向 asp.net core mvc 的 middleware ,问题不就解决了吗?

那怎么绕过去呢?不用 app.UseExceptionHandler ,自己写个 middleware ?先别做这个啥事,先搞清楚 app.UseExceptionHandler 究竟干了些啥?如果能通过 app.UseExceptionHandler 解决这个问题,岂不更好?

打开 app.UseExceptionHandler 所在的 github 仓库 Microsoft.AspNetCore.Diagnostics ,找到对应的中间件实现源码 ExceptionHandlerMiddleware ,下面的删减后的源码:

public class ExceptionHandlerMiddleware
{//... public async Task Invoke(HttpContext context){try{await _next(context);}catch (Exception ex){//...PathString originalPath = context.Request.Path;if (_options.ExceptionHandlingPath.HasValue){context.Request.Path = _options.ExceptionHandlingPath;}try{//...await _options.ExceptionHandler(context);                    //...
            }//...
        }}
}

原来在 ExceptionHandlerMiddleware 中只是将请求路径修改为 "/errors/500" 然后调用 ExceptionHandler ,但是我们在 app.UseExceptionHandler 只设置了请求路径,并没有设置 ExceptionHandler ,那就是默认  ExceptionHandler ,默认的 ExceptionHandler 是什么?

在 ExceptionHandlerMiddleware 的构造函数中找到了答案 —— 请求管线中的下一个中间件

if (_options.ExceptionHandler == null)
{//..._options.ExceptionHandler = _next;
}

只要将这里的 ExceptionHandler 修改为返回自定义错误页面的 handler ,问题就能解决。

ExceptionHandler 的类型是 RequestDelegate ,现在问题变成了如何在 RequestDelegate 中执行 mvc action 并返回响应内容?

在这个地方走了一些弯路,开始想通过 IActionResultExecutor 实现,但没成功。

后来想到最简单的方法就是利用已有的 mvc 中间件,基于 app.UseMvcWithDefaultRoute() 构建出 RequestDelegate 。基于这个思路,在 ExceptionHandlerExtensions 中以 Action<IApplicationBuilder> 为参数的扩展方法中学到一招:

var subAppBuilder = app.New();
configure(subAppBuilder);
var exceptionHandlerPipeline = subAppBuilder.Build();return app.UseExceptionHandler(new ExceptionHandlerOptions
{ExceptionHandler = exceptionHandlerPipeline
});

原来可以如此简单地通过 IApplicationBuilder 构建出 RequestDelegate 。

通过学习的这一招完美地解决了问题!

public static IApplicationBuilder UseCustomErrorPages(this IApplicationBuilder app)
{var options = new ExceptionHandlerOptions{ExceptionHandlingPath = "/errors/500",ExceptionHandler = app.New().UseMvcWithDefaultRoute().Build()};app.UseExceptionHandler(options);app.UseStatusCodePagesWithReExecute("/errors/{0}");return app;
}

解决 ASP.NET Core 自定义错误页面对 Middleware 异常无效的问题相关推荐

  1. [转载]针对IIS7以上的ASP.NET网站自定义错误页面与异常日志总结

    针对IIS7以上的ASP.NET网站自定义错误页面与异常日志总结 汪宇杰 2014-1-11 星期六 02:31 455 Reads 1 Comments 自定义错误页面和异常记录是个很古老的话题了, ...

  2. SpringBoot 错误页面使用、自定义错误页、自定义异常、自定义异常解析器

    在SpringBoot使用错误页面非常的简单 一. 错误页面使用 二. 自定义错误页 三.自定义异常 四.自定义异常解析器 一. 错误页面使用 只需要在templates里创建一个error文件夹,然 ...

  3. asp.net core 自定义基于 HttpContext 的 Serilog Enricher

    asp.net core 自定义基于 HttpContext 的 Serilog Enricher Intro 通过 HttpContext 我们可以拿到很多有用的信息,比如 Path/QuerySt ...

  4. asp.net core 自定义 Policy 替换 AllowAnonymous 的行为

    asp.net core 自定义 Policy 替换 AllowAnonymous 的行为 Intro 最近对我们的服务进行了改造,原本内部服务在内部可以匿名调用,现在增加了限制,通过 identit ...

  5. nginx自定义错误页

    文章目录 自定义错误页 1.为每种类型的错误设置单独的处理方式 2.利用在线资源进行处理错误 3.更改晌应状态码 4.设置错误页面案例 自定义错误页 在网站访问过程中,经常会遇见各种各样的错误,如找不 ...

  6. 升级到asp.net core 3.1遇到的json异常

    升级到asp.net core 3.1遇到的json异常 参考文章: (1)升级到asp.net core 3.1遇到的json异常 (2)https://www.cnblogs.com/Wadere ...

  7. SpringBoot系列五:SpringBoot错误处理(数据验证、处理错误页、全局异常)

    SpringBoot系列五:SpringBoot错误处理(数据验证.处理错误页.全局异常) 参考文章: (1)SpringBoot系列五:SpringBoot错误处理(数据验证.处理错误页.全局异常) ...

  8. ASP.NET Core WebAPI帮助页--Swagger简单使用1.0

    1.什么是Swagger? Swagger是一个规范且完整的框架,提供描述.生产.消费和可视化RESTful API,它是为了解决Web API生成有用文档和帮助页的问题. 2.为啥选用swagger ...

  9. asp.net core自定义依赖注入容器,替换自带容器

    依赖注入 在asp.net core程序中,众所周知,依赖注入基本上贯穿了整个项目,以通用的结构来讲解,控制器层(Controller层)依赖业务层(Service层),业务层依赖于仓储层(Repos ...

最新文章

  1. 笔试分享 | 带你解读校招人工智能笔试题
  2. 论一名项目经理的能力素养
  3. FineUIMvc随笔(1)动态创建表格列
  4. linux下创建和删除软、硬链接
  5. HTTP协议(HyperText Transfer Protocol,超文本传输协议)
  6. django中的分页
  7. 计算机网络总结之计算机概述
  8. 脚本其实很简单-windows配置核查程序(1)
  9. 【转】矩阵的几何解释
  10. ubuntu每日构建版
  11. 前端学习(2559):双向数据和单向数据不冲突
  12. 16进制数组转字符串
  13. win7需要计算机管理员权限,Win7系统提示“需要管理员权限”如何解决?
  14. 关于MyEclips新导入项目报错问题,解决方法!(基础)
  15. matlab条形图颜色矩阵,matlab中的条形图开关颜色
  16. WinCE6.0+ S3C6410 IIC驱动源码学习
  17. 柱状图之最大矩形面积
  18. MAC 下开发 不区分大小写问题及解决
  19. 【解决方案】SkeyeVSS石油油田钻井无线智能视频监控系统解决方案
  20. 如何利用PS动作一秒将字体生成逼真刺绣效果

热门文章

  1. python与php8-php8的扩展arginfo生成工具及工具初体验
  2. python里面的之前打过的记忆信息-python中的if __name__ == 'main'
  3. python3.6.5安装教程-Ubuntu16.04安装python3.6.5步骤详解
  4. python快速编程入门黑马-新手如何快速入门Python编程?/开发python入门教程
  5. python中文叫什么意思-python中文叫什么
  6. 解决错误:No module named ‘Cryptodome‘ 和错误rosbag.bag.ROSBagException: unsupported compression type: lz4
  7. 蓝桥哈夫曼树C语言,实验四 哈夫曼树及哈夫曼编码
  8. 使用Callable来获取数组的最大值
  9. ClassNotFoundException和 NoClassDefFoundError区别验证
  10. 谨慎跟随初始目的不被关联问题带偏