dotNet8 全局异常处理
前言
异常的处理在我们应用程序中是至关重要的,在 dotNet 中有很多异常处理的机制,比如MVC的异常筛选器, 管道中间件定义try catch捕获异常处理亦或者第三方的解决方案Hellang.Middleware.ProblemDetails等。MVC异常筛选器不太灵活,对管道的部分异常捕获不到,后两种方式大家项目应该经常出现。
在 dotNet8 发布之后支持了新的异常处理机制 IExceptionHandler或者UseExceptionHandler异常处理程序的lambda配置,配合dotNet7原生支持的ProblemDetail使得异常处理更加规范。
本文用一个简单的 Demo 带大家看一下新的异常处理方式
文末有示例完整的源代码
先起一个
WebApi的新项目
Problem Details
Problem Details是一种在HTTP API中用于描述错误信息的标准化格式。根据 RFC 7807,Problem Details 提供了一种统一、可机器读取的方式来呈现出发生在 API 请求中的问题。它包括各种属性,如 title、status、detail、type 等,用于清晰地描述错误的性质和原因。通过使用 Problem Details,开发人员可以为 API 的错误响应提供一致性和易于理解的结构化格式,从而帮助客户端更好地处理和解决问题。
项目中使用 Problem Details
builder.Services.AddProblemDetails();
如果我们不对异常进行捕获处理,Asp.Net Core 提供了两种不同的内置集中式机制来处理未经处理的异常
app.UseDeveloperExceptionPage();
开发人员异常中间件
开发人员异常中间件会显示服务器错误的详细堆栈跟踪,不建议在非开发环境显示,暴漏核心错误信息给客户端,有严重的安全风险
app.UseExceptionHandler(); 异常处理程序中间件,
使用异常处理程序中间件生成的是标准的简化回复
测试 UseDeveloperExceptionPage
var builder = WebApplication.CreateBuilder(args);
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen();
builder.Services.AddProblemDetails();
var app = builder.Build();
app.MapGet("/TestUseDeveloperExceptionPage",
() => { throw new Exception("测试UseDeveloperExceptionPage"); });
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseSwagger();
app.UseSwaggerUI();
app.UseDeveloperExceptionPage();// 开发人员异常页
}
app.UseStatusCodePages();
app.UseHttpsRedirection();
app.Run();
调用 TestUseDeveloperExceptionPage 接口
回参
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
"title": "System.Exception",
"status": 500,
"detail": "测试UseDeveloperExceptionPage",
"exception": {
"details": "System.Exception: 测试UseDeveloperExceptionPage\r\n at Program.<>c.<<Main>$>b__0_0() in C:\\dotNetParadise\\dot-net-paradise-exception\\dotNetParadise-Exception\\dotNetParadise-Exception\\Program.cs:line 7\r\n at lambda_method3(Closure, Object, HttpContext)\r\n at Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Diagnostics.DeveloperExceptionPageMiddlewareImpl.Invoke(HttpContext context)",
"headers": {
"Accept": [
"*/*"
],
"Host": [
"localhost:7130"
],
"User-Agent": [
"Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/122.0.0.0 Safari/537.36 Edg/122.0.0.0"
],
"Accept-Encoding": [
"gzip, deflate, br"
],
"Accept-Language": [
"en-US,en;q=0.9"
],
"Cookie": [
"ajs_anonymous_id=b96604ea-c096-4693-acfb-b3a9e8403f0e; Quasar_admin_Vue3_username=admin; Quasar_admin_Vue3_token=b1aa15b6-02bb-44b9-8668-0157a1d9b6f0; Quasar_admin_Vue3_lang=en-US"
],
"Referer": [
"https://localhost:7130/swagger/index.html"
],
"sec-ch-ua": [
"\"Chromium\";v=\"122\", \"Not(A:Brand\";v=\"24\", \"Microsoft Edge\";v=\"122\""
],
"sec-ch-ua-mobile": [
"?0"
],
"sec-ch-ua-platform": [
"\"Windows\""
],
"sec-fetch-site": [
"same-origin"
],
"sec-fetch-mode": [
"cors"
],
"sec-fetch-dest": [
"empty"
]
},
"path": "/TestUseDeveloperExceptionPage",
"endpoint": "HTTP: GET /TestUseDeveloperExceptionPage",
"routeValues": {}
}
可以看到所有的信息都抛出来给到了客户端,适合在开发环境用,非开发环境尤其是生产环境不要启用。
app.UseExceptionHandler();
异常处理程序中间件
// app.UseDeveloperExceptionPage();// 开发人员异常页
app.UseExceptionHandler();//异常处理中间件
测试一下
{
"type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
"title": "An error occurred while processing your request.",
"status": 500
}
可以看到只保留了最基本的报错信息,这样第一步我们已经完成了。
自定义异常 和 IExceptionHandler
创建一个自定义异常信息
public class CustomException(int code, string message) : Exception(message)
{
public int Code { get; private set; } = code;
public string Message { get; private set; } = message;
}
集成IExceptionHandler创建自定义异常处理器
public class CustomExceptionHandler(ILogger<CustomException> logger, IWebHostEnvironment environment) : IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
{
if (exception is not CustomException customException) return false;
logger.LogError(
exception, "Exception occurred: {Message} {StackTrace} {Source}", exception.Message, exception.StackTrace, exception.Source);
var problemDetails = new ProblemDetails
{
Status = customException.Code,
Title = customException.Message,
};
if (environment.IsDevelopment())
{
problemDetails.Detail = $"Exception occurred: {customException.Message} {customException.StackTrace} {customException.Source}";
}
httpContext.Response.StatusCode = problemDetails.Status.Value;
await httpContext.Response
.WriteAsJsonAsync(problemDetails, cancellationToken);
return true;
}
}
可以注册多个自定义异常处理器分别处理不同类型的异常,按默认的注册顺序来处理,如果返回
true则会处理此异常返回false会跳到下一个ExceptionHandler,没处理的异常在 UseExceptionHandler 中间件做最后处理。
创建第二个ExceptionHandler 处理系统异常
public class SystemExceptionHandle(ILogger<CustomException> logger, IWebHostEnvironment environment) : IExceptionHandler
{
public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
{
if (exception is CustomException) return false;
logger.LogError(
exception, "Exception occurred: {Message} {StackTrace} {Source}", exception.Message, exception.StackTrace, exception.Source);
var problemDetails = new ProblemDetails
{
Status = StatusCodes.Status500InternalServerError,
Title = "An error occurred while processing your request",
};
if (environment.IsDevelopment())
{
problemDetails.Detail = $"Exception occurred: {exception.Message} {exception.StackTrace} {exception.Source}";
}
httpContext.Response.StatusCode = problemDetails.Status.Value;
await httpContext.Response
.WriteAsJsonAsync(problemDetails, cancellationToken);
return true;
}
}
IOC 容器注册ExceptionHandler
builder.Services.AddExceptionHandler<CustomExceptionHandler>();
builder.Services.AddExceptionHandler<SystemExceptionHandle>();
新加接口测试一下
app.MapGet("/CustomThrow", () =>
{
throw new CustomException(StatusCodes.Status403Forbidden, "你没有权限!");
}).WithOpenApi();
回参
{
"title": "你没有权限!",
"status": 403,
"detail": "Exception occurred: 你没有权限! at Program.<>c.<<Main>$>b__0_1() in C:\\dotNetParadise\\dot-net-paradise-exception\\dotNetParadise-Exception\\dotNetParadise-Exception\\Program.cs:line 15\r\n at lambda_method5(Closure, Object, HttpContext)\r\n at Microsoft.AspNetCore.HttpsPolicy.HttpsRedirectionMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Diagnostics.StatusCodePagesMiddleware.Invoke(HttpContext context)\r\n at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddlewareImpl.<Invoke>g__Awaited|10_0(ExceptionHandlerMiddlewareImpl middleware, HttpContext context, Task task) dotNetParadise-Exception"
}
可以看出全局异常捕获生效了。
最后
本文讲的是 dotNet8 新的异常处理方式,当时也可以用UseExceptionHandler的lambda方式可以创建,但是不如这种强类型约束的规范,大家在升级 dotNet8 时可以参考本文来修改项目现有的全部异常捕获方式。
dotNet8 全局异常处理的更多相关文章
- mvc自定义全局异常处理
异常信息处理是任何网站必不可少的一个环节,怎么有效显示,记录,传递异常信息又成为重中之重的问题.本篇将基于上篇介绍的html2cancas截图功能,实现mvc自定义全局异常处理.先看一下最终实现效果: ...
- 在.NET Core程序中设置全局异常处理
以前我们想设置全局异常处理只需要这样的代码: AppDomain currentDomain = AppDomain.CurrentDomain; currentDomain.UnhandledExc ...
- springMvc全局异常处理
本文中只测试了:实现Spring的异常处理接口HandlerExceptionResolver 自定义自己的异常处理器 对已有代码没有入侵性等优点,同时,在异常处理时能获取导致出现异常的对象,有利于提 ...
- MVC 全局异常处理及禁用显示头
MVC网站的global.asax中的Application_Start方法里,有这样一段代码: public class MvcApplication : System.Web.HttpApplic ...
- Spring MVC 解决无法访问静态文件和"全局异常处理"
我们都知道,Spring MVC的请求都会去找controller控制器,若果我们页面中引入了一个外部样式,这样是没效果的, 我们引入样式的时候是通过<like href="...&q ...
- Spring Boot 2.x 系列教程:WebFlux REST API 全局异常处理 Error Handling
摘要: 原创出处 https://www.bysocket.com 「公众号:泥瓦匠BYSocket 」欢迎关注和转载,保留摘要,谢谢! 本文内容 为什么要全局异常处理? WebFlux REST 全 ...
- .NET MVC全局异常处理(二)
目录 .NET MVC全局异常处理(二) MVC过滤器Filter .NET MVC全局异常处理(二) 对上节的内容进行了补充 MVC过滤器Filter MVC有四种过滤器:Authorization ...
- .NET MVC全局异常处理(一)
目录 .NET MVC全局异常处理 IIS配置 静态错误页配置 .NET错误页配置 程序设置 全局异常配置 .NET MVC全局异常处理 一直知道有.NET有相关的配置,但没有实际做过,以为改下设定就 ...
- Spring Boot 全局异常处理
Spring Boot版本 1.5 @ControllerAdvice public class GlobalExceptionHandler extends ResponseEntityExcept ...
- SpringMVC 全局异常处理
在 JavaEE 项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度 ...
随机推荐
- Go语言实现八大排序|排序算法|超详细保姆级别注释
前言 那么这里博主先安利一些干货满满的专栏了! 首先是博主的高质量博客的汇总,这个专栏里面的博客,都是博主最最用心写的一部分,干货满满,希望对大家有帮助. 高质量博客汇总https://blog.cs ...
- [Docker] Dockerfile常用保留字
FROM 基础镜像,当前新镜像是基于哪个镜像的,指定一个已经存在的镜像作为模板.第一条必须是from MAINTAINER 镜像维护者的姓名和邮箱地址 RUN 容器构建时需要运行的命令,也就是在 do ...
- Asp .Net Core 系列:Asp .Net Core 集成 Panda.DynamicWebApi
目录 简介 Asp .Net Core 集成 Panda.DynamicWebApi 配置 原理 什么是POCO Controller? POCO控制器原理 ControllerFeatureProv ...
- gateway 是什么?
Gateway 定义是什么? 百度百科解释: 网关(Gateway)又称网间连接器.协议转换器.网关在网络层以上实现网络互连,是复杂的网络互连设备,仅用于两个高层协议不同的网络互连. 网关既可以用于广 ...
- 查看调整cpu频率及模式
使用cpufrequtils查看调整cpu频率及模式 cpufrequtils是一个查看和修改CPU频率GHz的工具 有些物理服务器使用默认频率进行运行,这时可以使用该工具进行就该CPU的核心频率 安 ...
- NC19782 Tree
题目链接 题目 题目描述 修修去年种下了一棵树,现在它已经有n个结点了. 修修非常擅长数数,他很快就数出了包含每个点的连通点集的数量. 澜澜也想知道答案,但他不会数数,于是他把问题交给了你. 输入描述 ...
- NC50959 To the Max
题目链接 题目 题目描述 Given a two-dimensional array of positive and negative integers, a sub-rectangle is any ...
- C 语言常用头文件解释
C系统提供了丰富的系统文件,称为库文件,整理一下以后好实用: <stdio.h> 定义了三个变量类型.一些宏和各种函数来执行输入和输出 https://www.runoob.com/cpr ...
- low-code 低代码平台 java 代码自动一键生成工具
low-code low-code 是一款为 java 打造的低代码平台. 开源地址:https://github.com/houbb/low-code 特性 支持基本的增删改查 支持枚举值处理 支持 ...
- win32 - WriteConsoleA 的使用
#include <Windows.h> #include <stdio.h> #pragma warning(disable:4996) void PasswordCheck ...