天天看點

ASP.NET Core應用的錯誤處理[2]:DeveloperExceptionPageMiddleware中間件如何呈現“開發者異常頁面”一、 處理編譯異常二、處理運作時異常

如上面的代碼片段所示,當我們建立一個DeveloperExceptionPageMiddleware對象的時候需要以參數的形式提供一個IOptions<DeveloperExceptionPageOptions>對象,而DeveloperExceptionPageOptions對象攜帶我們為這個中間件指定的配置選項,具體的配置選項展現在如下另個屬性(FileProvider和SourceCodeLineCount)。

一般來說我們總是通過調用ApplicationBuilder的擴充方法UseDeveloperExceptionPage方法來注冊這個DeveloperExceptionPageMiddleware中間件,這兩個擴充方法重載采用如下的方式建立并注冊這個DeveloperExceptionPageMiddleware中間件。

DeveloperExceptionPageMiddleware中間件處理的兩種異常類型說起。總的來說,該中間件處理的異常大體上可以分為兩類,它們分别是“運作時異常”和“編譯異常”,後者類型實作了ICompilationException接口,如下的代碼片段基本上展現了異常處理在DeveloperExceptionPageMiddleware中間件中的實作。

我想很多人會很疑惑:我們編寫一個ASP.NET

Core應用應該是先編譯成程式集,然後再部署并啟動執行,為什麼運作過程中還會出現“編譯異常”呢?從ASP.NET

Core應用層面來說,我們采用的是“預編譯”,也就說我們部署的不是源代碼而是編譯好的程式集,是以運作過程中根本就不存在“編譯異常”一說。但是不要忘了在一個ASP.NET

Core

MVC應用中,視圖檔案(.cshtml)是支援“動态編譯”的。也就是說我們可以直接部署視圖源檔案,應用在執行過程中是可以動态地編譯它們的。換句話說,由于視圖檔案支援動态編譯,我們是可以在部署環境直接修改視圖檔案的。

對于DeveloperExceptionPageMiddleware中間件來說,對于普通的運作時異常,它會采用HTML文檔的形式将異常自身的詳細資訊和目前請求的資訊以HTML文檔的形式呈現出來,我們前面示範的執行個體已經很好的說明了這一點。如果應用在動态編譯視圖檔案中出現了編譯異常,最終呈現出來的錯誤頁面将具有不同的結構和内容,我們不防也通過一個簡單的執行個體來示範一下DeveloperExceptionPageMiddleware中間件針對編譯異常的處理。

我們通過如下所示的代碼啟動了一個ASP.NET Core

MVC應用,并通過調用ApplicationBuilder的擴充方法UseDeveloperExceptionPage注冊了DeveloperExceptionPageMiddleware中間件。對應定義在HomeController中的Action方法Index來說,它會負責将對應的視圖呈現出來。

根據約定,Action方法Index呈現出來的視圖檔案對應的路徑應該是“~/views/home/index.cshtml”,我們為此在這個路徑下建立這個視圖檔案。為了能夠在動态編譯過程中出現編譯異常,我們在這個視圖檔案中編寫了如下三行代碼,Foobar是一個尚未被建立的類型。

當我們利用浏覽器通路HomeController的Action方法Index的時候,應用會動态編譯目标視圖,由于視圖檔案中使用了一個不曾不定義的類型,動态編譯會失敗,響應的錯誤資訊會以如圖7所示的形式出現在浏覽器上。可以看出錯誤頁面顯示的内容和結構與前面示範的執行個體是完全不一樣的,我們不僅可以從這個錯誤頁面中得到導緻編譯失敗的視圖檔案的路徑(“Views/Home/Index.cshtml”),還可以直接看到導緻編譯失敗的那一行代碼。不僅如此,這個錯誤頁面還直接将參與編譯的源代碼(不是定義在.cshtml檔案中的原始代碼,而是經過轉換處理生成的C#代碼)。毫無疑問,這個如此詳盡的錯誤頁面對于相信開發人員的糾錯針對是非常有價值的。

ASP.NET Core應用的錯誤處理[2]:DeveloperExceptionPageMiddleware中間件如何呈現“開發者異常頁面”一、 處理編譯異常二、處理運作時異常

一般來說,動态編譯的整個過程由兩個步驟組成,它先是将源代碼(類似于.cshtml這樣的模闆檔案)轉換成針對某種.NET語言(比如C#)的代碼,然後進一步地編譯成IL代碼。動态編譯過程中抛出的異常類型一般會實作ICompilationException接口。如下面的代碼片段所示,該接口值具有一個唯一的屬性CompilationFailures,它傳回一個元素類型為CompilationFailure的集合。編譯失敗的相關資訊被封裝在一個CompilationFailure對象之中,我們可以利用它得到源檔案的路徑(SourceFilePath)和内容(SourceFileContent),以及源代碼轉換後傳遞編譯的内容。如果在内容轉換過程就已經發生錯誤,那麼SourceFileContent屬性可能傳回Null。

CompilationFailure類型還具有一個名為Messages的隻讀屬性,它傳回一個元素類型為DiagnosticMessage的集合,一個DiagnosticMessage對象承載着一些描述編譯錯誤的診斷資訊。我們不僅可以借助DiagnosticMessage對象的相關屬性得到描述編譯錯誤的消息(Message和FormattedMessage),還可以得到發生編譯錯誤所在源檔案的路徑(SourceFilePath)以及範圍,StartLine、StartColumn、EndLine和EndColumn屬性分别表示導緻編譯錯誤的源代碼在源檔案中開始和結束的行與列(行數和列數分别從1和0開始計數)。

從上圖可以看出,錯誤頁面會直接将導緻編譯失敗的相關源代碼顯示出來。具體來說,它不僅僅會将直接導緻失敗的源代碼實作出來,還會同時顯示前後相鄰的源代碼。至于相鄰源代碼應該顯示多少行,實際上是通過DeveloperExceptionPageOptions的SourceCodeLineCount屬性控制的。

對于前面示範的這個執行個體來說,如果我們希望前後相鄰的三行代碼被顯示在錯誤頁面上,我們可以采用如上的方式為注冊的DeveloperExceptionPageMiddleware中間件指定一個DeveloperExceptionPageOptions對象,并将它的SourceCodeLineCount屬性設定為3。與此同時,我們将視圖檔案(index.cshtml)改寫成如下的形式,即在導緻編譯失敗的那一行代碼前後分别添加了4行代碼。

對于定義在視圖檔案中的共計9行代碼,根據在注冊DeveloperExceptionPageMiddleware中間件時指定的規則,最終顯示在錯誤頁面上的應該是第2行到第8行。如果利用浏覽器通路相同的位址,我們會看到這7行代碼會以下圖的形式出現在錯誤頁面上。值得一提的是,如果我們沒有對SourceCodeLineCount屬性作顯式設定,它的預設值為6。

ASP.NET Core應用的錯誤處理[2]:DeveloperExceptionPageMiddleware中間件如何呈現“開發者異常頁面”一、 處理編譯異常二、處理運作時異常

對于錯誤頁面呈現的描述異常的詳細資訊,除了類型和消息這些基本的資訊之外,異常的堆棧追蹤(Stack

Trace)也會出現在該頁面中。不僅如此,如果堆棧追蹤包含源代碼的資訊(比如源檔案路徑以及對應源代碼所在的行和列),DeveloperExceptionPageMiddleware中間件還會試着加載源檔案,并将導緻異常的源代碼原封不動的顯示出來。

我們将前面示範的代碼改寫成如上的形式,并在本地以Debug模式運作該程式,将會得到如下圖所示的錯誤頁面。我們會看到由于異常的堆棧追蹤資訊中包含源代碼的相關資訊(源檔案路徑和行号),是以導緻異常的那一行代碼可以原封不動地顯示出來。與編譯異常處理方式一樣,一并顯示出來的還包括與之相鄰的代碼,至于具體會顯示多少行相鄰代碼,自然也是通過DeveloperExceptionPageOptions的SourceCodeLineCount屬性來控制的。

ASP.NET Core應用的錯誤處理[2]:DeveloperExceptionPageMiddleware中間件如何呈現“開發者異常頁面”一、 處理編譯異常二、處理運作時異常

DeveloperExceptionPageOptions的FileProvider提供FileProvider對象的目的就是幫助讀取源檔案的内容,或者說它為我們的糾錯調試提供源檔案。如果我們在建立DeveloperExceptionPageMiddleware中間件的時候沒有顯式提供這麼一個FileProvider,那麼預設情況下會使用指向ContentRoot目錄的這個PhysicalFileProvider。值得一提的是,如果異常的追蹤堆棧中出現了源檔案的路徑,DeveloperExceptionPageMiddleware中間件總是會試圖先從本地檔案系統去加載這個檔案,隻有在本地檔案加載失敗的情況下它才會利用指定的FileProvider去讀取檔案。

<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-2.html" target="_blank">原文連結</a>

繼續閱讀