dotNet8 全局異常處理

来源:https://www.cnblogs.com/ruipeng/p/18075123
-Advertisement-
Play Games

前言 異常的處理在我們應用程式中是至關重要的,在 dotNet 中有很多異常處理的機制,比如MVC的異常篩選器, 管道中間件定義try catch捕獲異常處理亦或者第三方的解決方案Hellang.Middleware.ProblemDetails等。MVC異常篩選器不太靈活,對管道的部分異常捕獲不到 ...


前言

異常的處理在我們應用程式中是至關重要的,在 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 新的異常處理方式,當時也可以用UseExceptionHandlerlambda方式可以創建,但是不如這種強類型約束的規範,大家在升級 dotNet8 時可以參考本文來修改項目現有的全部異常捕獲方式。

Demo 源代碼

dotNet 官網教程

本文來自博客園,作者:董瑞鵬,轉載請註明原文鏈接:https://www.cnblogs.com/ruipeng/p/18075123


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • 前言 想開發一些小工具,所以想系統性的學習一遍aardio,之前都是哪裡不會搜哪裡,順便寫些教程。我的主要語言是Python,所以會以Python作為對比來加深印象。 aardio的基礎語法和JavaScript基本類似,如果你學過JavaScript,aardio很容易上手。下麵的文檔來自官方文檔 ...
  • 源生成器是 C# 9 中引入的一項功能,允許在編譯過程中動態生成代碼。 它們直接與 C# 編譯器集成(Roslyn)併在編譯時運行,分析源代碼並根據分析結果生成附加代碼。 源生成器提供了一種簡化的自動化代碼生成方法,無需外部工具或單獨的預編譯步驟。 通過無縫集成到編譯過程中,源生成器可以提高生產力、 ...
  • 這是一個我個人寫的庫,主要實現的是基於tcpclient的網站外擴網盤的解決方案,可以使用家用網路外掛個人電腦中的資源到自己的網站上,已經上傳nuget,大家可以直接在nuget包管理中搜索到,直接搜索ZmjNetDisk即可,下麵介紹具體的使用方式: 另外一提這個庫做的比較的個人化,因為他就是為了 ...
  • 1. 渲染系統概述 WPF 採用保留模式渲染系統 (Retained Mode Rendering System),該系統可分為 UI 線程和複合線程兩個主要部分,兩者協作完成 WPF 應用程式的渲染工作。 1.1 立即模式GUI和保持模式GUI 圖形 API 可分為保留模式API 和即時模式API ...
  • 在開發各種應用程式時,我們經常需要對文件系統中的文件或文件夾進行實時監測,以便在文件內容改變、文件被創建或刪除時能夠及時做出反應。在 C# 中,System.IO.FileSystemWatcher 類為我們提供了這樣一個強大的功能。 一、引入 FileSystemWatcher 類 首先,在項目中 ...
  • 我個人編寫的庫,在我個人網站,小程式等很多地方都在使用中,大家可以搜索小程式 什邡市宅貓君網路工作室 或者到我的網站 store.zhaimaojun.cn 去體驗支付和登錄效果。 本庫主要實現了native pay(二維碼支付)jsapi pay(小程式直接調起支付),需要註意的是這是基於api3 ...
  • 這是我自己個人編寫的日誌記錄,主要使用在只需要記錄日誌,偶爾到文件中查看一下日誌記錄的情況。我自己寫的一些服務之類的是使用了這個的,代碼很少,使用很簡單。 第一步 搜索和安裝我的Nuget包 搜索和安裝zmjtool這個包,我寫的,如下圖: 第二步 引入namespace和創建logger對象 1 ...
  • 支持.Net/.Net Core/.Net Framework,可以部署在Docker, Windows, Linux, Mac。 RabbitMQ作為一款主流的消息隊列工具早已廣受歡迎。相比於其它的MQ工具,RabbitMQ支持的語言更多、功能更完善。 1.發送消息、獲取消息、使用消息 本文提供一 ...
一周排行
    -Advertisement-
    Play Games
  • 基於.NET Framework 4.8 開發的深度學習模型部署測試平臺,提供了YOLO框架的主流系列模型,包括YOLOv8~v9,以及其系列下的Det、Seg、Pose、Obb、Cls等應用場景,同時支持圖像與視頻檢測。模型部署引擎使用的是OpenVINO™、TensorRT、ONNX runti... ...
  • 十年沉澱,重啟開發之路 十年前,我沉浸在開發的海洋中,每日與代碼為伍,與演算法共舞。那時的我,滿懷激情,對技術的追求近乎狂熱。然而,隨著歲月的流逝,生活的忙碌逐漸占據了我的大部分時間,讓我無暇顧及技術的沉澱與積累。 十年間,我經歷了職業生涯的起伏和變遷。從初出茅廬的菜鳥到逐漸嶄露頭角的開發者,我見證了 ...
  • C# 是一種簡單、現代、面向對象和類型安全的編程語言。.NET 是由 Microsoft 創建的開發平臺,平臺包含了語言規範、工具、運行,支持開發各種應用,如Web、移動、桌面等。.NET框架有多個實現,如.NET Framework、.NET Core(及後續的.NET 5+版本),以及社區版本M... ...
  • 前言 本文介紹瞭如何使用三菱提供的MX Component插件實現對三菱PLC軟元件數據的讀寫,記錄了使用電腦模擬,模擬PLC,直至完成測試的詳細流程,並重點介紹了在這個過程中的易錯點,供參考。 用到的軟體: 1. PLC開發編程環境GX Works2,GX Works2下載鏈接 https:// ...
  • 前言 整理這個官方翻譯的系列,原因是網上大部分的 tomcat 版本比較舊,此版本為 v11 最新的版本。 開源項目 從零手寫實現 tomcat minicat 別稱【嗅虎】心有猛虎,輕嗅薔薇。 系列文章 web server apache tomcat11-01-官方文檔入門介紹 web serv ...
  • 1、jQuery介紹 jQuery是什麼 jQuery是一個快速、簡潔的JavaScript框架,是繼Prototype之後又一個優秀的JavaScript代碼庫(或JavaScript框架)。jQuery設計的宗旨是“write Less,Do More”,即倡導寫更少的代碼,做更多的事情。它封裝 ...
  • 前言 之前的文章把js引擎(aardio封裝庫) 微軟開源的js引擎(ChakraCore))寫好了,這篇文章整點js代碼來測一下bug。測試網站:https://fanyi.youdao.com/index.html#/ 逆向思路 逆向思路可以看有道翻譯js逆向(MD5加密,AES加密)附完整源碼 ...
  • 引言 現代的操作系統(Windows,Linux,Mac OS)等都可以同時打開多個軟體(任務),這些軟體在我們的感知上是同時運行的,例如我們可以一邊瀏覽網頁,一邊聽音樂。而CPU執行代碼同一時間只能執行一條,但即使我們的電腦是單核CPU也可以同時運行多個任務,如下圖所示,這是因為我們的 CPU 的 ...
  • 掌握使用Python進行文本英文統計的基本方法,並瞭解如何進一步優化和擴展這些方法,以應對更複雜的文本分析任務。 ...
  • 背景 Redis多數據源常見的場景: 分區數據處理:當數據量增長時,單個Redis實例可能無法處理所有的數據。通過使用多個Redis數據源,可以將數據分區存儲在不同的實例中,使得數據處理更加高效。 多租戶應用程式:對於多租戶應用程式,每個租戶可以擁有自己的Redis數據源,以確保數據隔離和安全性。 ...