︿
Top

2024年2月23日 星期五

ASP.NET MVC 及 ASP.NET Core MVC 的錯誤處理

Error Handling in ASP.NET MVC and ASP.NET Core MVC

前言

有效的錯誤處理, 對於確保應用程式的穩定性和用戶體驗, 是非常重要的; 若系統沒有作好錯誤處理, 而呈現程式錯誤細節給使用者, 代表系統不穩定, 且用戶體驗會非常糟糕.

若您覺得這篇文章對您有幫助, 請不吝於在筆者的 GitHub Repository (可在 Repository 下載範例程式) 上按星星, 謝謝.

如果只關心 ASP.NET Core MVC 的實作程序及程式碼, 可以直接看筆者的另一篇文章: ASP.NET Core MVC Error Handling 摘要版 .

(一) 名詞定義

1.. 錯誤(Error): 係指框架 (ASP.NET 或 ASP.NET Core), 或應用程式自定義的例外 (Exception). 本文會說明如何將未通過資料檢核的錯誤, 包裝為自定義用戶端例外拋出. 以提供一致性的錯誤訊息規格.

2.. 錯誤處理(Error Handling): 係指錯誤取得 (Catch), 訊息規格 (Specification), 呈現 (Presentation), 及記錄 (Logging). 本文主要著重在錯誤取得及一致性錯誤訊息規格制訂.

(二) 錯誤取得機制概要

ASP.NET Core 提供了中介軟體 (Middleware) 的機制, 讓開發人員可以在整個 HTTP Request / Response 的過程中, 進行例外的攔截.

MVC 框架本身 (含 ASP.NET MVC 及 ASP.NET Core MVC) 提供了例外過濾器 (Exception Filter) 的機制, 讓開發人員在 HTTP 訊息進入 MVC 的生命週後, 進行例外的攔截.

理解 (Microsoft Learn) Filters in ASP.NET Core 的 2 張圖, 對開發人員而言, 是非常重要的.

若要完全處理整個 Web 應用程式的例外, 實作方面的相關配套如下:

1.. ASP.NET MVC: 要搭配 Web.config 或 Global.asax + 自定義的 ErrorController.

2.. ASP.NET Core MVC: 可以自行撰寫 ASP.NET Core 的例外處理中介軟體 (Middleware).
說明: Middleware 是屬於 ASP.NET Core, 不是 ASP.NET Core MVC; ASP.NET Core MVC 是建置在 ASP.NET Core 上的一個框架.

3.. ASP.NET Core 8 以後提供了實作 IExceptionHandler 介面的方式, 簡化例外處理 Middleware 的撰寫, 內建的 app.UseExceptionHandler(), 其實也是一佪 Middleware, 會使用前述的類別, 攔截例外. 只是筆者能力有限, 目前遇到無法取得 routeData (ex: controller, action 名稱) 的問題, 雖然本文還是有提供範例, 但暫不推薦此方式.

(三) 一致性的錯誤訊息規格概要

回傳一致性的錯誤訊息, 對於系統的開發及對用戶體驗, 是非常有幫助的.

1.. ASP.NET MVC: 只能自定義錯誤訊息類別.

2.. ASP.NET Core MVC: 可採用自定義錯誤訊息類別, 或內建的 ProblemDetails 類別.
說明: ASP.NET Core 2.1 起, 有一個依 IETF >RFC7807 規範 設計的 ProblemDetails 類別, 用以統一回傳結果. 但也可視需求, 自行定義統一的回傳類別.
說明: 本文採自定義的 ErrorViewModel 類別作為回傳格式, 作為說明之用. 但最後仍補上採用 ProblemDetails 類別的範例.

伺服端程式必須進行資料檢核, 才能去存取資料庫. 若資料檢核發現有誤, 代表用戶端沒有作好資料檢核的工作, 或者被瀏覽器的 F12 破解, 因此屬於用戶端的問題 (HTTP 4XX).

這類的錯誤, 需設計繼承自 Exception 類別的自定義例外類別, 當發生未通過檢核時, 打包為自訂例外拋出, 由前述的 Middleware 或 Exception Filter 攔截及處理.

上述的文字內容的思路, 可參考結論的流程圖.

(四) 章節內容

本文將探討 ASP.NET MVC 及 ASP.NET Core MVC 中一些錯誤處理實作技巧.

一. 預設的錯誤處理方式
二. 自定義統一的回傳的錯誤訊息規格
三. 自定義 Middleware, 用以產生貫穿 Request 軌跡的 TraceId
四. 撰寫 自定義例外 (Custom Exception), 供後續 Middleware 或 Filter 使用
五. 自定義 Middleware 攔截例外 (ASP.NET Core)
六. 建立 Exception Filter (ASP.NET MVC or ASP.NET Core MVC)
七. 實作 IExceptionHandler (ASP.NET Core 8 and later)
八. 建立另一個 MVC 專案, 參考前述的例外處理專案

目前 GitHub 上的範例版本為最後一版, 請下載參考. 輸出結果, 若發現回傳值有 JSON 欄位名稱字首大小寫的差異 (例如: TraceId vs. traceId), 或者有增加欄位 (例如: ControllerName, ActionName), 請再自行參酌程式碼. 前者是因為在輸出 JSON 時, 有對 System.Text.Json 作了一些選項的設定; 後者是因為要寫 Log 時, 需要該欄位.

2024年2月1日 星期四

如何在 ASP.NET Core 6 MVC 撰寫具有 相依注入(DI) 的自訂屬性 (Custom Attribute)

How to write Custom Attribute with DI in ASP.NET Core 6 MVC

前言

由於 ASP.NET Core 6 MVC 專案採用 DI (Dependency Injection) 的方式, 有一些相關的 自訂屬性 (Custom Attribute) 也想採用 DI.

原本程式碼是在 MyLogAttribute 裡面自行 new Logger(), 在套用屬性時, 是用 [MyLogAttribute]; 改採 DI 後, 變成建構子要傳入 ILogger 的參數, 在 HomeController 的 Index() method, 要改成以下的方式, 但編譯會出現 CS0181 及 CS0120 的編譯錯誤.

[MyLogAttribute(_logger)]
public IActionResult Index() {
    return View();
}

參考文件1.. 為 CS0181 的說明, 它提到自訂屬性傳入的參數, 不可以是物件變數, 必須為常數值.
本文主要採 參考文件2.. 的方法2 (ServiceFilter) 進行演練及實作.

完整範例可由 GitHub 下載.