dotNet8 全局非常处理

打印 上一主题 下一主题

主题 845|帖子 845|积分 2535

前言

非常的处理在我们应用步伐中是至关重要的,在 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
  1. builder.Services.AddProblemDetails();
复制代码
如果我们不对非常进行捕获处理,Asp.Net Core 提供了两种不同的内置集中式机制来处理未经处理的非常

  • app.UseDeveloperExceptionPage();
    开辟人员非常中间件
    开辟人员非常中间件会显示服务器错误的详细堆栈跟踪,不建议在非开辟环境显示,暴漏核心错误信息给客户端,有严重的安全风险

  • app.UseExceptionHandler(); 非常处理步伐中间件,
    利用非常处理步伐中间件生成的是标准的简化回复
测试 UseDeveloperExceptionPage
  1. var builder = WebApplication.CreateBuilder(args);
  2. builder.Services.AddEndpointsApiExplorer();
  3. builder.Services.AddSwaggerGen();
  4. builder.Services.AddProblemDetails();
  5. var app = builder.Build();
  6. app.MapGet("/TestUseDeveloperExceptionPage",
  7.     () => { throw new Exception("测试UseDeveloperExceptionPage"); });
  8. // Configure the HTTP request pipeline.
  9. if (app.Environment.IsDevelopment())
  10. {
  11.     app.UseSwagger();
  12.     app.UseSwaggerUI();
  13.     app.UseDeveloperExceptionPage();// 开发人员异常页
  14. }
  15. app.UseStatusCodePages();
  16. app.UseHttpsRedirection();
  17. app.Run();
复制代码
调用 TestUseDeveloperExceptionPage 接口
回参
  1. {
  2.   "type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
  3.   "title": "System.Exception",
  4.   "status": 500,
  5.   "detail": "测试UseDeveloperExceptionPage",
  6.   "exception": {
  7.     "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)",
  8.     "headers": {
  9.       "Accept": [
  10.         "*/*"
  11.       ],
  12.       "Host": [
  13.         "localhost:7130"
  14.       ],
  15.       "User-Agent": [
  16.         "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"
  17.       ],
  18.       "Accept-Encoding": [
  19.         "gzip, deflate, br"
  20.       ],
  21.       "Accept-Language": [
  22.         "en-US,en;q=0.9"
  23.       ],
  24.       "Cookie": [
  25.         "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"
  26.       ],
  27.       "Referer": [
  28.         "https://localhost:7130/swagger/index.html"
  29.       ],
  30.       "sec-ch-ua": [
  31.         ""Chromium";v="122", "Not(A:Brand";v="24", "Microsoft Edge";v="122""
  32.       ],
  33.       "sec-ch-ua-mobile": [
  34.         "?0"
  35.       ],
  36.       "sec-ch-ua-platform": [
  37.         ""Windows""
  38.       ],
  39.       "sec-fetch-site": [
  40.         "same-origin"
  41.       ],
  42.       "sec-fetch-mode": [
  43.         "cors"
  44.       ],
  45.       "sec-fetch-dest": [
  46.         "empty"
  47.       ]
  48.     },
  49.     "path": "/TestUseDeveloperExceptionPage",
  50.     "endpoint": "HTTP: GET /TestUseDeveloperExceptionPage",
  51.     "routeValues": {}
  52.   }
复制代码
可以看到所有的信息都抛出来给到了客户端,得当在开辟环境用,非开辟环境尤其是生产环境不要启用。
app.UseExceptionHandler();

非常处理步伐中间件
  1. // app.UseDeveloperExceptionPage();// 开发人员异常页
  2. app.UseExceptionHandler();//异常处理中间件
复制代码
测试一下
  1. {
  2.   "type": "https://tools.ietf.org/html/rfc9110#section-15.6.1",
  3.   "title": "An error occurred while processing your request.",
  4.   "status": 500
  5. }
复制代码
可以看到只保存了最根本的报错信息,这样第一步我们已经完成了。
自定义非常 和 IExceptionHandler

创建一个自定义非常信息
  1. public class CustomException(int code, string message) : Exception(message)
  2. {
  3.     public int Code { get; private set; } = code;
  4.     public string Message { get; private set; } = message;
  5. }
复制代码
集成IExceptionHandler创建自定义非常处理器
  1. public class CustomExceptionHandler(ILogger<CustomException> logger, IWebHostEnvironment environment) : IExceptionHandler
  2. {
  3.     public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
  4.     {
  5.         if (exception is not CustomException customException) return false;
  6.         logger.LogError(
  7.               exception, "Exception occurred: {Message} {StackTrace} {Source}", exception.Message, exception.StackTrace, exception.Source);
  8.         var problemDetails = new ProblemDetails
  9.         {
  10.             Status = customException.Code,
  11.             Title = customException.Message,
  12.         };
  13.         if (environment.IsDevelopment())
  14.         {
  15.             problemDetails.Detail = $"Exception occurred: {customException.Message} {customException.StackTrace} {customException.Source}";
  16.         }
  17.         httpContext.Response.StatusCode = problemDetails.Status.Value;
  18.         await httpContext.Response
  19.             .WriteAsJsonAsync(problemDetails, cancellationToken);
  20.         return true;
  21.     }
  22. }
复制代码
可以注册多个自定义非常处理器分别处理不同类型的非常,按默认的注册顺序来处理,如果返回true则会处理此非常返回false会跳到下一个ExceptionHandler,没处理的非常在 UseExceptionHandler 中间件做末了处理。
创建第二个ExceptionHandler 处理体系非常
  1. public class SystemExceptionHandle(ILogger<CustomException> logger, IWebHostEnvironment environment) : IExceptionHandler
  2. {
  3.     public async ValueTask<bool> TryHandleAsync(HttpContext httpContext, Exception exception, CancellationToken cancellationToken)
  4.     {
  5.         if (exception is CustomException) return false;
  6.         logger.LogError(
  7.               exception, "Exception occurred: {Message} {StackTrace} {Source}", exception.Message, exception.StackTrace, exception.Source);
  8.         var problemDetails = new ProblemDetails
  9.         {
  10.             Status = StatusCodes.Status500InternalServerError,
  11.             Title = "An error occurred while processing your request",
  12.         };
  13.         if (environment.IsDevelopment())
  14.         {
  15.             problemDetails.Detail = $"Exception occurred: {exception.Message} {exception.StackTrace} {exception.Source}";
  16.         }
  17.         httpContext.Response.StatusCode = problemDetails.Status.Value;
  18.         await httpContext.Response
  19.             .WriteAsJsonAsync(problemDetails, cancellationToken);
  20.         return true;
  21.     }
  22. }
复制代码
IOC 容器注册ExceptionHandler
  1. builder.Services.AddExceptionHandler<CustomExceptionHandler>();
  2. builder.Services.AddExceptionHandler<SystemExceptionHandle>();
复制代码
新加接口测试一下
  1. app.MapGet("/CustomThrow", () =>
  2. {
  3.     throw new CustomException(StatusCodes.Status403Forbidden, "你没有权限!");
  4. }).WithOpenApi();
复制代码
回参
  1. {
  2.   "title": "你没有权限!",
  3.   "status": 403,
  4.   "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"
  5. }
复制代码
可以看出全局非常捕获见效了。
末了

本文讲的是 dotNet8 新的非常处理方式,当时也可以用UseExceptionHandler的lambda方式可以创建,但是不如这种强类型约束的规范,大家在升级 dotNet8 时可以参考本文来修改项目现有的全部非常捕获方式。
Demo 源代码
dotNet 官网教程

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

悠扬随风

金牌会员
这个人很懒什么都没写!

标签云

快速回复 返回顶部 返回列表