天天看點

ASP.NET Core應用的錯誤處理[1]:三種呈現錯誤頁面的方式一、顯示開發者異常頁面二、顯示定制異常頁面三、針對響應狀态碼定制錯誤頁面

由于ASP.NET

Core應用是一個同時處理多個請求的伺服器應用,是以在處理某個請求過程中抛出的異常并不會導緻整個應用的終止。出于安全方面的考量,為了避免敏感資訊的外洩,用戶端在預設的情況下并不會得到詳細的出錯資訊,這無疑會在開發環境下增加查錯糾錯的難度。對于生産環境來說,我們也希望最終使用者能夠根據具體的錯誤類型得到具有針對性并且友好的錯誤消息。ASP.NET

目錄 一、顯示開發者異常頁面 二、顯示定制異常頁面 三、針對響應狀态碼定制錯誤頁面

一般情況下,如果ASP.NET Core在處理某個請求時出現異常,它一般會傳回一個狀态碼為“500

Internal Server

Error”的響應。為了避免一些敏感資訊的外洩,詳細的錯誤資訊并不會随着響應發送給用戶端,是以用戶端隻會得到一個很一般化的錯誤消息。以如下這個程式為例,服務端在處理每個請求時都會抛出一個類型為InvalidOperationException的異常。

當我們利用浏覽器通路這個應用的時候,總是會得到如下圖所示的這個錯誤頁面。可以看出這個頁面僅僅告訴我們目标應用目前無法正常處理本次請求,除了提供的響應狀态碼(“HTTP ERROR 500”)之外,它并沒有提供任何有益于差錯糾錯的錯誤資訊。

ASP.NET Core應用的錯誤處理[1]:三種呈現錯誤頁面的方式一、顯示開發者異常頁面二、顯示定制異常頁面三、針對響應狀态碼定制錯誤頁面

那麼有人可能會覺得雖然浏覽器上沒有顯示出任何詳細的錯誤資訊,也許它會隐藏在接收到的HTTP響應封包中。針對通過浏覽器放出的這個請求,得到的響應内容如下所示,我們會發現響應封包根本沒有主體部分,有限的幾個報頭也并沒有承載任何與錯誤有關的資訊。

由于應用并沒有中斷,浏覽器上也并沒有顯示任何具有針對性的錯誤資訊,開發人員在進行查錯糾錯的時候如何準确定位到作為錯誤根源的那一行代碼呢?具體來說,我們又兩種解決方案,一種就是利用日志,因為ASP.NET Core在進行請求處理時出現的任何錯誤都會被寫入日志,是以我們可以通過注冊相應的LoggerProvider(比如注冊一個ConsoleLoggerProvider将日志直接寫入宿主應用的控制台)到來擷取寫入的錯誤日志。

至于另一種解決方案,就是直接顯示一個包含錯誤相應資訊的錯誤頁面,由于這個頁面是在開發環境給開發者看的,是以我們将這個頁面稱為“開發者異常頁面(Developer Exception Page)”。針對頁面的自動呈現是利用一個名為DeveloperExceptionPageMiddleware的中間件來完成的,我們可以調用ApplicationBuilder的擴充方法UseDeveloperExceptionPage來注冊這個中間件。

一旦注冊了這個DeveloperExceptionPageMiddleware中間件,ASP.NET Core應用在處理請求出現的異常資訊就會以下圖的形式直接出現在浏覽器上,我們可以在這個頁面中看到幾乎所有的錯誤資訊,包括異常的類型、消息和堆棧資訊等。

ASP.NET Core應用的錯誤處理[1]:三種呈現錯誤頁面的方式一、顯示開發者異常頁面二、顯示定制異常頁面三、針對響應狀态碼定制錯誤頁面

開發者異常頁面除了顯示與抛出的異常相關的資訊之外,還會以如下圖所示的形式顯示與目前請求上下文相關的資訊,其中包括目前請求URL攜帶的所有查詢字元串、所有請求報頭以及Cookie的内容。如此詳盡的資訊無疑會極大地幫助開發人員盡快地找出錯誤的根源。

ASP.NET Core應用的錯誤處理[1]:三種呈現錯誤頁面的方式一、顯示開發者異常頁面二、顯示定制異常頁面三、針對響應狀态碼定制錯誤頁面

通過DeveloperExceptionPageMiddleware中間件呈現的錯誤頁面僅僅是供開發人員使用的,詳細的錯誤資訊往往會攜帶一些敏感的資訊,是以務必記住隻有在開發環境才能注冊這個中間件,如下所示的代碼片段展現了針對DeveloperExceptionPageMiddleware中間件正确的注冊方式。

DeveloperExceptionPageMiddleware中間件通過将異常詳細資訊和基于目前請求的内容直接呈現在錯誤頁面中,這為開發人員的糾錯診斷提供了極大的便利。但是在生産環境下,我們傾向于為最終的使用者呈現一個定制的錯誤頁面,而這可以通過注冊另一個名為ExceptionHandlerMiddleware的中間件來實作。顧名思義,這個中間件旨在提供一個異常處理器(Exception

Handler)來處理抛出的異常。實際上這個所謂的異常處理器就是一個類型為RequestDelegate的委托對象,ExceptionHandlerMiddleware中間件捕捉到抛出的異常後利用它來響應目前的請求。

還是以上面建立的這個總是會抛出一個 InvalidOperationException異常的應用為例。我們按照如下的形式調用ApplicationBuilder的擴充方法UseExceptionHandler注冊了上述的這個ExceptionHandlerMiddleware中間件。這個擴充方法具有一個ExceptionHandlerOptions類型的參數,它的ExceptionHandler屬性傳回的就是這個作為異常處理器的RequestDelegate對象。

如上面的代碼片段所示,這個作為異常處理器的RequestDelegate僅僅是将一個簡單的錯誤消息(“Unhandled

exception

occurred!”)作為響應的内容。當我們利用浏覽器通路該應用的時候,這個定制的錯誤消息将會以如圖4所示的形式直接呈現在浏覽器上。

ASP.NET Core應用的錯誤處理[1]:三種呈現錯誤頁面的方式一、顯示開發者異常頁面二、顯示定制異常頁面三、針對響應狀态碼定制錯誤頁面

最終作為異常處理器的是一個類型為RequestDelegate的委托對象,而ApplicationBuilder具有建立這個委托對象的能力。具體來說,我們可以根據異常處理的需要将相應的中間件注冊到某個ApplicationBuilder對象上,并最終利用這個ApplicationBuilder根據注冊的中間件建立出作為異常處理器的RequestDelegate對象。

如果異常處理需要通過一個或者多個中間件來完成,我們可以按照如下的形式調用另一個UseExceptionHandler方法重載。這個方法的參數類型為Action<IApplicationBuilder>,我們調用它的Run方法注冊了一個中間件來響應一個簡單的錯誤消息。

上面這兩種異常處理的形式都展現在提供一個RequestDelegate的委托對象來處理抛出的異常并完成最終的響應。如果應用已經設定了一個錯誤頁面,并且這個錯誤頁面具有一個固定的路徑,那麼我們在進行異常處理的時候就沒有必要提供這個RequestDelegate對象,而隻需要重定向到錯誤頁面指向的路徑即可。這種采用服務端重定向的異常處理方式可以采用如下的形式調用另一個UseExceptionHandler方法重載來完成,這個方法的參數表示的就是重定向的目标路徑(“/error”),我們針對這個路徑注冊了一個路由來響應定制的錯誤消息。

由于Web應用采用HTTP通信協定,是以我們應該盡可能低迎合HTTP标準并将定義在協定規範中的語義應用到應用中。對于異常或者錯誤的語義表達在HTTP協定層面主要展現在響應封包的狀态碼上,具體來說HTTP通信的錯誤大體分為如下兩種類型:

用戶端錯誤:表示因用戶端提供不正确的請求資訊而導緻伺服器不能正常處理請求,響應狀态碼範圍在400~499之間。

服務端錯誤:表示伺服器在處理請求過程中因自身的問題而發生錯誤,響應狀态碼在500~509之間。

正是因為響應狀态碼是對錯誤或者異常語義最重要的表達,是以在很多情況下我們需要針對不同的響應狀态碼來定制顯示的錯誤資訊。針對響應狀态碼對錯誤頁面的定制可以借助一個類型為StatusCodePagesMiddleware的中間件來實作,我們可以調用ApplicationBuilder相應的擴充方法來注冊這個中間件。

DeveloperExceptionPageMiddleware和ExceptionHandlerMiddleware中間件都是在後續請求處理過程中抛出異常的情況下才會被調用,而StatusCodePagesMiddleware被調用的前提是後續請求助理過程中産生一個錯誤響應狀态碼(範圍在400~599之間)。如果僅僅希望顯示一個統一的錯誤頁面,我們可以按照如下的形式調用擴充方法UseStatusCodePages注冊這個中間件,傳入該方法的兩個參數分别表示響應采用的媒體類型和主體内容。

如上面的代碼片段所示,應用在處理請求的時候總是會将響應狀态碼設定為500,是以最終的響應内容将由注冊的StatusCodePagesMiddleware中間件來提供。我們調用UseStatusCodePages方法的時候将響應的媒體類型設定為“text/plain”,并将一段簡單的錯誤消息作為了響應的主體内容。值得一提的時候,作為響應内容的字元串可以包含一個占位符({0}),StatusCodePagesMiddleware中間件最終會采用目前響應狀态碼來替換它。如果我們利用浏覽器來通路這個應用,将會得到如下圖所示的錯誤頁面。

ASP.NET Core應用的錯誤處理[1]:三種呈現錯誤頁面的方式一、顯示開發者異常頁面二、顯示定制異常頁面三、針對響應狀态碼定制錯誤頁面

如果我們希望針對不同的錯誤狀态碼顯示不同的錯誤頁面,那麼我們就需要将具體的請求處理邏輯實作在一個的狀态碼錯誤處理器中,并最終提供給StatusCodePagesMiddleware中間件。這個所謂的狀态碼錯誤處理器展現為一個類型為Func<StatusCodeContext,

Task>的委托對象,作為輸入的StatusCodeContext對象是對目前HttpContext的封裝,同時承載着其他一些與錯誤處理相關的選項設定,我們将在本系列後續部分對這個類型進行詳細介紹。

對于如下這個應用來說,它在處理任意一個請求是總是會随機地選擇一個400~599之間的整數作為響應的狀态碼,是以用戶端傳回的響應内容總是通過注冊的StatusCodePagesMiddleware中間件來提供。我們在調用另一個UseStatusCodePages方法重載的時候,為注冊的中間件指定了一個Func<StatusCodeContext,

Task>對象作為狀态碼錯誤處理器。

我們指定的狀态碼錯誤處理器在處理請求的時候,根據響應狀态碼将錯誤分成用戶端錯誤和服務端錯誤兩種類型,并選擇針對性的錯誤消息作為響應内容。當我們利用浏覽器通路這個應用的時候,顯示的錯誤消息将由響應狀态碼來決定。

ASP.NET Core應用的錯誤處理[1]:三種呈現錯誤頁面的方式一、顯示開發者異常頁面二、顯示定制異常頁面三、針對響應狀态碼定制錯誤頁面

在ASP.NET

Core的世界裡,針對請求的處理總是展現為一個RequestDelegate對象。如果請求的處理需要借助一個或者多個中間件來完成,我們可以将它們注冊到ApplicationBuilder對象上并利用它将中間件管道轉換成一個RequestDelegate對象。用于注冊StatusCodePagesMiddleware中間件的UseStatusCodePage方法還具有另一個重載,它允許我們采用這種方式來建立一個RequestDelegate對象來完成最終的請求處理工作,是以上面示範的這個應用完全可以改寫成如下的形式。

<a href="http://www.cnblogs.com/artech/p/error-handling-in-asp-net-core-1.html">ASP.NET Core應用的錯誤處理[1]:三種呈現錯誤頁面的方式</a>

<a href="http://www.cnblogs.com/artech/p/error-handling-in-asp-net-core-2.html">ASP.NET Core應用的錯誤處理[2]:DeveloperExceptionPageMiddleware中間件</a>

<a href="http://www.cnblogs.com/artech/p/error-handling-in-asp-net-core-3.html">ASP.NET Core應用的錯誤處理[3]:ExceptionHandlerMiddleware中間件</a>

<a href="http://www.cnblogs.com/artech/p/error-handling-in-asp-net-core-4.html">ASP.NET Core應用的錯誤處理[4]:StatusCodePagesMiddleware中間件</a>

作者:蔣金楠

微信公衆賬号:大内老A

如果你想及時得到個人撰寫文章以及著作的消息推送,或者想看看個人推薦的技術資料,可以掃描左邊二維碼(或者長按識别二維碼)關注個人公衆号(原來公衆帳号蔣金楠的自媒體将會停用)。

本文版權歸作者和部落格園共有,歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接,否則保留追究法律責任的權利。

<a href="http://www.cnblogs.com/artech/p/error-handling-in-asp-net-core-1.html" target="_blank">原文連結</a>

繼續閱讀