【踩坑】.NET 8.0 自定义IExceptionHandler不生效

net,iexceptionhandler · 浏览次数 : 0

小编点评

在ASP.NET Core中,实现全局异常处理可以通过以下几种方式: 1. 使用内置的异常处理中间件 在Startup类中添加以下代码: ```csharp app.UseExceptionHandler(appError => { appError.Run(async context => { context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; context.Response.ContentType = "application/json"; var contextFeature = context.Features.Get(); if (contextFeature != null) { logger.LogError($"Something went wrong: {contextFeature.Error}"); await context.Response.WriteAsync(new ErrorDetails() { StatusCode = context.Response.StatusCode, Message = "Internal Server Error." }.ToString()); } }); }); ``` 2. 自定义一个中间件 创建一个名为`ExceptionMiddleware`的类,继承自`RequestDelegate`,并在构造函数中传入`ILoggerManager`。在`InvokeAsync`方法中处理异常: ```csharp public class ExceptionMiddleware { private readonly RequestDelegate _next; private readonly ILoggerManager _logger; public ExceptionMiddleware(RequestDelegate next, ILoggerManager logger) { _logger = logger; _next = next; } public async Task InvokeAsync(HttpContext httpContext) { try { await _next(httpContext); } catch (Exception ex) { _logger.LogError($"Something went wrong: {ex}"); await HandleExceptionAsync(httpContext, ex); } } private async Task HandleExceptionAsync(HttpContext context, Exception exception) { context.Response.ContentType = "application/json"; context.Response.StatusCode = (int)HttpStatusCode.InternalServerError; await context.Response.WriteAsync(new ErrorDetails() { StatusCode = context.Response.StatusCode, Message = "Internal Server Error from the custom middleware." }.ToString()); } } ``` 3. 使用过滤器(IExceptionFilter)实现 创建一个名为`CustomExceptionHandler`的类,继承自`IExceptionHandler`,并在`TryHandleAsync`方法中处理异常: ```csharp public class CustomExceptionHandler : IExceptionHandler { private readonly ILogger logger; public CustomExceptionHandler(ILogger logger) { this.logger = logger; } public ValueTask TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken) { var exceptionMessage = exception.Message; logger.LogError($"Error Message: {exceptionMessage}, Time of occurrence {DateTime.UtcNow}"); // Return false to continue with the default behavior // - or - return true to signal that this exception is handled return ValueTask.FromResult(false); } } ``` 在Startup类中添加以下代码以注册自定义的异常处理器: ```csharp builder.Services.AddExceptionHandler(); ``` 备注:在ASP.NET Core 8.0及更高版本中,建议使用IExceptionHandler接口实现异常处理。

正文

中间件实现异常处理

在ASP.NET Core里,我们可以使用中间件(Middleware)实现全局的异常处理。 如内置的异常处理中间件 UseExceptionHandler

app.UseExceptionHandler(appError =>
        {
            appError.Run(async context =>
            {
                context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
                context.Response.ContentType = "application/json";
                var contextFeature = context.Features.Get<IExceptionHandlerFeature>();
                if (contextFeature != null)
                {
                    logger.LogError($"Something went wrong: {contextFeature.Error}");
                    await context.Response.WriteAsync(new ErrorDetails()
                    {
                        StatusCode = context.Response.StatusCode,
                        Message = "Internal Server Error."
                    }.ToString());
                }
            });
        });

或者如下干脆完全自定义一个中间件

public class ExceptionMiddleware
{
    private readonly RequestDelegate _next;
    private readonly ILoggerManager _logger;
    public ExceptionMiddleware(RequestDelegate next, ILoggerManager logger)
    {
        _logger = logger;
        _next = next;
    }
    public async Task InvokeAsync(HttpContext httpContext)
    {
        try
        {
            await _next(httpContext);
        }
        catch (Exception ex)
        {
            _logger.LogError($"Something went wrong: {ex}");
            await HandleExceptionAsync(httpContext, ex);
        }
    }
    private async Task HandleExceptionAsync(HttpContext context, Exception exception)
    {
        context.Response.ContentType = "application/json";
        context.Response.StatusCode = (int)HttpStatusCode.InternalServerError;
        await context.Response.WriteAsync(new ErrorDetails()
        {
            StatusCode = context.Response.StatusCode,
            Message = "Internal Server Error from the custom middleware."
        }.ToString());
    }
}

当然还可以使用过滤器(Filter)来实现,即IExceptionFilter, 这些都能很方便实现自定义的全局异常处理。

IExceptionHandler 实现异常处理

在.NET 8.0里,微软新引入的IExceptionHandler也可以实现同样的功能,看官方的异常处理文档IExceptionHandler似乎已经是最推荐的异常处理方法。
在微软新开的项目里,IExceptionHandler正被广泛使用,比如Semantic Kernel
image
下面是官方文档给的一个简单实现

using Microsoft.AspNetCore.Diagnostics;

namespace ErrorHandlingSample
{
    public class CustomExceptionHandler : IExceptionHandler
    {
        private readonly ILogger<CustomExceptionHandler> logger;
        public CustomExceptionHandler(ILogger<CustomExceptionHandler> logger)
        {
            this.logger = logger;
        }
        public ValueTask<bool> TryHandleAsync(
            HttpContext httpContext,
            Exception exception,
            CancellationToken cancellationToken)
        {
            var exceptionMessage = exception.Message;
            logger.LogError(
                "Error Message: {exceptionMessage}, Time of occurrence {time}",
                exceptionMessage, DateTime.UtcNow);
            // Return false to continue with the default behavior
            // - or - return true to signal that this exception is handled
            return ValueTask.FromResult(false);
        }
    }
}

使用也很简单

builder.Services.AddExceptionHandler<CustomExceptionHandler>();

然而,如果你按照文档实现完之后,你所期待的自定义异常处理并不会生效,你必须添加一个空的lambda给UseExceptionHandlerAddExceptionHandler才会生效:)

app.UseExceptionHandler(o => { });

为什么会这样?看这个https://github.com/dotnet/aspnetcore/issues/51888, 去年下半年就有人发现了,微软尝试修复https://github.com/dotnet/aspnetcore/pull/51898, 但是改动有breaking change给拒绝了,加上有上面提到的workaround,就一直搁置了。
image
虽然又不是不能用,但是看着难受,开头就介绍了,UseExceptionHandler是启用内置的异常处理中间件,AddExceptionHandler是用来注册.NET 8.0新引入的IExceptionHandler,这个神奇workaround太一言难尽,欢迎踊跃提交PR去fix,先到先得☺

与【踩坑】.NET 8.0 自定义IExceptionHandler不生效相似的内容:

【踩坑】.NET 8.0 自定义IExceptionHandler不生效

中间件实现异常处理 在ASP.NET Core里,我们可以使用中间件(Middleware)实现全局的异常处理。 如内置的异常处理中间件 UseExceptionHandler app.UseExceptionHandler(appError => { appError.Run(async cont

使用 SQL 的方式查询消息队列数据以及踩坑指南

![Pulsar-sql.png](https://s2.loli.net/2023/08/30/3iz9yqfuSCn18xk.png) # 背景 为了让业务团队可以更好的跟踪自己消息的生产和消费状态,需要一个类似于表格视图的消息列表,用户可以直观的看到发送的消息;同时点击详情后也能查到消息的整个

记一次 .NET某账本软件 非托管泄露分析

一:背景 1. 讲故事 中秋国庆长假结束,哈哈,在老家拍了很多的短视频,有兴趣的可以上B站观看:https://space.bilibili.com/409524162 ,今天继续给大家分享各种奇奇怪怪的.NET生产事故,希望能帮助大家在未来的编程之路上少踩坑。 话不多说,这篇看一个.NET程序集泄

记一次 .NET某医疗器械清洗系统 卡死分析

一:背景 1. 讲故事 前段时间协助训练营里的一位朋友分析了一个程序卡死的问题,回过头来看这个案例比较经典,这篇稍微整理一下供后来者少踩坑吧。 二:WinDbg 分析 1. 为什么会卡死 因为是窗体程序,理所当然就是看主线程此时正在做什么? 可以用 ~0s ; k 看一下便知。 0:000> k #

关于.Net 6.0 在Linux ,Docker容器中,不安装任何依赖就生成图形验证码!!!!!!!!!!!

在.Net Framework时代,我们生成验证码大多都是用System.Drawing。 在.Net 6中使用也是没有问题的。 但是,System.Drawing却依赖于Windows GDI+。 为了实现跨平台,我陷入了沉思!! 微软推荐使用SkiaSharp 进行替代,所以就开始了,踩坑之旅

如何让 WinDebug Preview 加载 Dotnet Core 的 SOS.dll 进行调试

一、前言 最近我在使用 WinDebug进行系统调试,也是在学习《Net高级调试》这本书。以前听过 WinDebug 调试器,但是没有使用过,由于最近想起来了,就好好的研究一下,学习一下。初次接触,还是走了不少弯路,踩了不少坑。关于 WinDebug 最新版的安装方法,可以在“微软商店”里面,直接查

诞生记(一)——上线一个小程序最低要花多少钱?

我是一个很懒的人,很少写博客。为什么?因为技术发展太快了,刚学习记录下来过段时间来看看,发现全都过时了。太浪费感情了。 曾经我也是一个软粉,一个.Net开发者,同学都入坑Android、Java踩着时代的红利拿高薪的时候。我却始终爱着微软。一直到微软彻底抛弃Windows Phone10的时候我才死

踩坑记录:windows11下使用 VS2022 和 PCL1.14.1 配置点云开发环境

闲话不多说,具体在windows下下载PCL与解压pcl可以看https://www.yuque.com/huangzhongqing/pcl/这位大佬的文章,那我就具体说一下踩过点坑: 踩坑点1: 按照大佬的文章的步骤进行解压与下载,我的PCL环境下在了K盘中,但是最后不知怎么的我的openni2

SpringBoot 3.x 结合 Swagger3 (Knife4j )踩坑实录

SpringBoot 3.x + Swagger3 踩坑实录 我的是springboot 版本是:3.2.2 org.springframework.boot spring-boot-starter-parent

oidc-client.js踩坑吐槽贴

前言 前面选用了IdentityServer4做为认证授权的基础框架,感兴趣的可以看上篇<微服务下认证授权框架的探讨>,已经初步完成了authorization-code与implicit的简易demo(html+js 在IIS部署的站点),并实现了SSO,本想着将Demo迁移到vue工程是轻而易举