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 项目的开发中,不管是对底层的数据库操作过程,还是业务层的处理过程,还是控制层的处理过程,都不可避免会遇到各种可预知的.不可预知的异常需要处理.每个过程都单独处理异常,系统的代码耦合度 ...
随机推荐
- 高可用linux 服务器搭建
最原始的服务部署,为单点部署,即直接把服务部署在一个服务器上.如果服务器出现故障,或者服务因为某个异常而挂掉,则服务就会发生中断.单点部署出现故障的概率最高. 后来,出现了网关,比如 nginx ko ...
- korean doll likeness模型|Japanese-doll-likeness模型获取及使用
1.模型 之前给大家写了Mac安装stable-diffusion-webui绘制AI妹子保姆级教程,教程在下面 [奶奶看了也不会]AI绘画 Mac安装stable-diffusion-webui绘制 ...
- (C语言)1到50的阶乘之和列表,参考用,洛谷:P1009 [NOIP1998 普及组] 阶乘之和
1到50列表,阶乘之和 S=1!+2!+3!+⋯+n!(n≤50) 1::1 2::3 3::9 4::33 5::153 6::873 7::5913 8::46233 9::409113 10:: ...
- 单片机 IAP 功能进阶开发篇之BOOT升级(一)
引言 目的 主要介绍单片机 IAP 开发的设计思路,如何不使用下载烧录器的方式对单片机的程序进行升级,升级区域包括 bootloader 和用户程序的升级,升级方式有 UASRT 通信.CAN 通信和 ...
- [Ngbatis源码学习][SpringBoot] 由BeanFactoryPostProcessor想到
由BeanFactoryPostProcessor想到 在看Ngbatis源码时看到了对BeanFactoryPostProcessor后置处理器的使用,对其的使用并不是很了解,在此做一些学习和总结. ...
- Python_json类方法
Python_json类方法 import requests import json headers = { "User-Agent": "Mozilla/5.0 (Li ...
- SSD 接口简介——SATA/SAS
SSD盘和主机端通过各种各样的接口互联,这里种类比较多,而且有些既是接口又是协议,有些既是接口又是规范,所以很容易一头雾水,于是这篇文章主要目的是梳理好这些不同的概念,只是梳理并不做深入讲解. 一. ...
- Java并发编程实例--13.方法同步(synchronized)
使用synchronized关键字去控制对某个方法的并发调用. 某一时段内,只能有一个线程可以读取该方法. 其他线程需要等待前面线程调用完毕后方可调用. 不过,静态方法有着不同的行为. 虽然也是每次只 ...
- 常见的问题系列--- Swagger @ApiOperationSupport忽略失效的问题
https://www.cnblogs.com/hujunwei/p/15853307.html
- 小程序threejs参考
之前做了一个小程序眼镜试戴的功能,涉及了人脸识别和3D模型渲染等.暂时记录一些参考的东西,有时间再整理. threejs官方文档(一定要看看) https://threejs.org/docs/ind ...