處理未處理的例外狀況 (VB)
檢視或下載範例程式碼 \(英文\) (如何下載)
當生產環境中 Web 應用程式發生運行時錯誤時,請務必通知開發人員並記錄錯誤,以便在稍後的時間點進行診斷。 本教學課程提供 ASP.NET 如何處理運行時錯誤的概觀,並查看一種方式,讓自定義程式代碼在未處理的例外狀況反升至 ASP.NET 運行時間時執行。
簡介
在 ASP.NET 應用程式中發生未處理的例外狀況時,它會反升至 ASP.NET 運行時間,這會引發 Error
事件並顯示適當的錯誤頁面。 錯誤頁面有三種不同的類型: (YSOD) 的運行時間錯誤黃色畫面;例外狀況詳細數據 YSOD;和自定義錯誤頁面。 在 上述教學課程中 ,我們已將應用程式設定為針對遠端使用者使用自定義錯誤頁面,以及針對在本機瀏覽的使用者使用例外狀況詳細數據 YSOD。
使用符合網站外觀和操作的易記自定義錯誤頁面,是預設運行時間錯誤 YSOD 的慣用頁面,但顯示自定義錯誤頁面只是完整錯誤處理解決方案的一部分。 在生產環境中的應用程式發生錯誤時,請務必通知開發人員錯誤,讓他們能夠取消例外狀況的原因並加以解決。 此外,應該記錄錯誤的詳細數據,以便在稍後的時間點檢查和診斷錯誤。
本教學課程示範如何存取未處理的例外狀況詳細數據,以便記錄和開發人員通知。 在此教學課程之後的兩個教學課程會探索錯誤記錄連結庫,在一些設定之後,會自動通知開發人員運行時間錯誤並記錄其詳細數據。
注意
如果您需要以某種唯一或自定義的方式處理未處理的例外狀況,本教學課程中檢查的資訊最有用。 如果您只需要記錄例外狀況並通知開發人員,使用錯誤記錄連結庫就是要執行的方式。 接下來的兩個教學課程提供兩個這類連結庫的概觀。
引發事件時執行Error
程序代碼
事件會提供一種機制,讓對象發出訊號,指出發生有趣的專案,以及讓另一個物件執行回應中的程序代碼。 身為 ASP.NET 開發人員,您習慣以事件來思考。 如果您想要在訪客按下特定 Button 時執行一些程式代碼,您可以為該 Button Click
事件建立事件處理程式,並將程式代碼放在該處。 假設 ASP.NET 運行時間會在發生未處理的例外狀況時引發其 Error
事件 ,因此會遵循記錄錯誤詳細數據的程式代碼會在事件處理程式中。 但您要如何為 Error
事件建立事件處理程式?
事件Error
是 類別中許多事件之HttpApplication
一,這些事件會在要求存留期期間於 HTTP 管線的特定階段引發。 例如,HttpApplication
BeginRequest
類別的事件會在每個要求開始時引發;當安全性模塊識別出要求者時,就會引發其 AuthenticateRequest
事件。 這些 HttpApplication
事件可讓頁面開發人員在要求存留期的各個點上執行自定義邏輯。
事件的事件處理程式 HttpApplication
可以放在名為 Global.asax
的特殊檔案中。 若要在網站中建立此檔案,請使用名為的全域應用程式類別範本 Global.asax
,將新專案新增至網站的根目錄。
圖 1:新增 Global.asax
至 Web 應用程式
(按兩下即可檢視完整大小的影像)
Visual Studio 所建立檔案的內容和結構 Global.asax
會根據您使用 Web 應用程式專案 (WAP) 或網站專案 (WSP) 而略有不同。 使用 WAP 時,會 Global.asax
實作為兩個不同的檔案 - Global.asax
和 Global.asax.vb
。 檔案Global.asax
只包含參考.vb
檔案的指示詞;感興趣的事件處理程式定義於 檔案中Global.asax.vb
@Application
。 對於 WSP,只會建立單一檔案, Global.asax
而且事件處理程式會在 區塊中 <script runat="server">
定義。
Global.asax
由 Visual Studio 的全域應用程式類別範本在 WAP 中建立的檔案包含名為 Application_BeginRequest
、 Application_AuthenticateRequest
和 Application_Error
的事件處理程式,分別是事件BeginRequest
、 AuthenticateRequest
和 Error
的事件處理程式HttpApplication
。 另外還有名為 Application_Start
、 Session_Start
、 Application_End
和 Session_End
的事件處理程式,這些事件處理程式會在 Web 應用程式啟動時、新工作階段啟動時、應用程式結束時,以及工作階段分別結束時引發的事件處理程式。 Global.asax
Visual Studio 在 WSP 中建立的檔案只Application_Error
包含 、Application_Start
、Session_Start
、 Application_End
和 Session_End
事件處理程式。
注意
部署 ASP.NET 應用程式時,您必須將 Global.asax
檔案複製到生產環境。 在 Global.asax.vb
WAP 中建立的檔案不需要複製到生產環境,因為此程式代碼會編譯成專案的元件。
Visual Studio 的全域應用程式類別範本所建立的事件處理程式並不詳盡。 您可以藉由命名事件處理程式 ,為任何 HttpApplication
事件新增事件處理程式 Application_EventName
。 例如,您可以將下列程式代碼新增至 檔案, Global.asax
以建立 AuthorizeRequest
事件的事件處理程式:
Sub Application_AuthorizeRequest(ByVal sender As Object, ByVal e As EventArgs)
' Event handler code
End Sub
同樣地,您可以移除不需要的全域應用程式類別範本所建立的任何事件處理程式。 在本教學課程中,我們只需要事件的事件處理程式 Error
;您可以隨意從 Global.asax
檔案中移除其他事件處理程式。
注意
HTTP 模組 提供另一種方式來定義事件的事件處理程式 HttpApplication
。 HTTP 模組會建立為類別檔案,可直接放在 Web 應用程式專案內,或分成不同的類別庫。 由於它們可以分隔成類別庫,因此 HTTP 模組提供更有彈性且可重複使用的模型來建立 HttpApplication
事件處理程式。 Global.asax
雖然檔案專屬於其所在的 Web 應用程式,但 HTTP 模組可以編譯成元件,此時將 HTTP 模組新增至網站就像在資料夾中卸除元件Bin
一樣簡單,並在 中Web.config
註冊模組。 本教學課程不會探討如何建立和使用 HTTP 模組,但下列兩個教學課程中使用的兩個錯誤記錄連結庫會實作為 HTTP 模組。 如需 HTTP 模組優點的詳細資訊,請參閱 使用 HTTP 模組和處理程式建立可插入式 ASP.NET 元件。
擷取未處理例外狀況的相關信息
此時,我們有具有事件處理程式的 Application_Error
Global.asax 檔案。 當此事件處理程式執行時,我們需要通知開發人員錯誤並記錄其詳細數據。 若要完成這些工作,我們必須先判斷所引發例外狀況的詳細數據。 使用 Server 物件的 GetLastError
方法來 擷取導致引發 Error
事件之未處理的例外狀況詳細數據。
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
' Get the error details
Dim lastErrorWrapper As HttpException = _
CType(Server.GetLastError(), HttpException)
End Sub
方法會GetLastError
傳回 類型的 Exception
物件,這是 .NET Framework 中所有例外狀況的基底類型。 不過,在上述程序代碼中,我正在將 所GetLastError
HttpException
傳回的Exception物件轉換成物件。 Error
如果因為處理 ASP.NET 資源期間擲回例外狀況而引發事件,則擲回的HttpException
例外狀況會包裝在 內。 若要取得產生 Error 事件的實際例外狀況,請使用 InnerException
屬性。 Error
如果事件因為 HTTP 型例外狀況而引發,例如不存在頁面的要求,則會擲回 ,HttpException
但沒有內部例外狀況。
下列程式代碼會使用 GetLastErrormessage
來擷取觸發 Error
事件之例外狀況的相關信息,並將 HttpException
儲存在名為 lastErrorWrapper
的變數中。 然後,它會將原始例外狀況的類型、訊息和堆棧追蹤儲存在三個字元串變數中,檢查 lastErrorWrapper
是否為在 HTTP 型例外狀況 (觸發事件的實際 Error
例外狀況) ,或者它只是處理要求時擲回之例外狀況的包裝函式。
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
' Get the error details
Dim lastErrorWrapper As HttpException = _
CType(Server.GetLastError(), HttpException)
Dim lastError As Exception = lastErrorWrapper
If lastErrorWrapper.InnerException IsNot Nothing Then
lastError = lastErrorWrapper.InnerException
End If
Dim lastErrorTypeName As String = lastError.GetType().ToString()
Dim lastErrorMessage As String = lastError.Message
Dim lastErrorStackTrace As String = lastError.StackTrace
End Sub
此時,您有將例外狀況詳細數據記錄到資料庫數據表所需的所有資訊。 您可以針對每個感興趣的錯誤詳細數據建立包含數據行的資料庫數據表,包括類型、訊息、堆疊追蹤等等,以及其他有用的資訊片段,例如所要求頁面的 URL 和目前登入的用戶名稱。 在事件處理程式中 Application_Error
,您接著會連接到資料庫,並將記錄插入數據表中。 同樣地,您可以新增程式碼,以透過電子郵件向開發人員發出錯誤警示。
接下來兩個教學課程中檢查的錯誤記錄連結庫提供現成可用的功能,因此不需要自行建置此錯誤記錄和通知。 不過,為了說明正在 Error
引發事件,而且 Application_Error
事件處理程式可用來記錄錯誤詳細數據並通知開發人員,讓我們新增程式代碼,以在發生錯誤時通知開發人員。
發生未處理的例外狀況時通知開發人員
在生產環境中發生未處理的例外狀況時,請務必警示開發小組,以便評估錯誤並判斷需要採取的動作。 例如,如果聯機到資料庫時發生錯誤,則您必須再次檢查您的 連接字串,而且或許是向您的 Web 主控公司開啟支援票證。 如果因為程式設計錯誤而發生例外狀況,可能需要新增額外的程式代碼或驗證邏輯,以避免未來發生這類錯誤。
命名空間中的 System.Net.Mail
.NET Framework 類別可讓您輕鬆地傳送電子郵件。 類別MailMessage
代表電子郵件訊息,並具有、From
、Subject
Body
、 和 Attachments
等To
屬性。 SmtpClass
可用來使用指定的 SMTP 伺服器傳送 MailMessage
物件;您可以在 中的 專案中Web.config file
以程式設計方式或宣告方式<system.net>
指定 SMTP 伺服器設定。 如需在 ASP.NET 應用程式中傳送電子郵件訊息的詳細資訊,請參閱我的文章:從 ASP.NET Web Pages 網站傳送 Email,以及 System.Net.Mail 常見問題。
注意
專案 <system.net>
包含傳送電子郵件時類別 SmtpClient
所使用的 SMTP 伺服器設定。 您的 Web 主控公司可能會有一部 SMTP 伺服器,可用來從應用程式傳送電子郵件。 如需您應該在 Web 應用程式中使用的 SMTP 伺服器設定資訊,請參閱 Web 主機的支援一節。
將下列程式代碼新增至事件處理程式, Application_Error
以在發生錯誤時傳送電子郵件給開發人員:
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
' Get the error details
Dim lastErrorWrapper As HttpException = _
CType(Server.GetLastError(), HttpException)
Dim lastError As Exception = lastErrorWrapper
If lastErrorWrapper.InnerException IsNot Nothing Then
lastError = lastErrorWrapper.InnerException
End If
Dim lastErrorTypeName As String = lastError.GetType().ToString()
Dim lastErrorMessage As String = lastError.Message
Dim lastErrorStackTrace As String = lastError.StackTrace
Const ToAddress As String = "support@example.com"
Const FromAddress As String = "support@example.com"
Const Subject As String = "An Error Has Occurred!"
' Create the MailMessage object
Dim mm As New MailMessage(FromAddress, ToAddress)
mm.Subject = Subject
mm.IsBodyHtml = True
mm.Priority = MailPriority.High
mm.Body = string.Format( _
"<html>" & vbCrLf & _
" <body>" & vbCrLf & _
" <h1>An Error Has Occurred!</h1>" & vbCrLf & _
" <table cellpadding=""5"" cellspacing=""0"" border=""1"">" & vbCrLf & _
" <tr>" & vbCrLf & _
" <tdtext-align: right;font-weight: bold"">URL:</td>" & vbCrLf & _
" <td>{0}</td>" & vbCrLf & _
" </tr>" & vbCrLf & _
" <tr>" & vbCrLf & _
" <tdtext-align: right;font-weight: bold"">User:</td>" & vbCrLf & _
" <td>{1}</td>" & vbCrLf & _
" </tr>" & vbCrLf & _
" <tr>" & vbCrLf & _
" <tdtext-align: right;font-weight: bold"">Exception Type:</td>" & vbCrLf & _
" <td>{2}</td>" & vbCrLf & _
" </tr>" & vbCrLf & _
" <tr>" & vbCrLf & _
" <tdtext-align: right;font-weight: bold"">Message:</td>" & vbCrLf & _
" <td>{3}</td>" & vbCrLf & _
" </tr>" & vbCrLf & _
" <tr>" & vbCrLf & _
" <tdtext-align: right;font-weight: bold"">Stack Trace:</td>" & vbCrLf & _
" <td>{4}</td>" & vbCrLf & _
" </tr> " & vbCrLf & _
" </table>" & vbCrLf & _
" </body>" & vbCrLf & _
"</html>", _
Request.RawUrl, _
User.Identity.Name, _
lastErrorTypeName, _
lastErrorMessage, _
lastErrorStackTrace.Replace(Environment.NewLine, "<br />"))
'Attach the Yellow Screen of Death for this error
Dim YSODmarkup As String = lastErrorWrapper.GetHtmlErrorMessage()
If Not String.IsNullOrEmpty(YSODmarkup) Then
Dim YSOD As Attachment = _
Attachment.CreateAttachmentFromString(YSODmarkup, "YSOD.htm")
mm.Attachments.Add(YSOD)
End If
' Send the email
Dim smtp As New SmtpClient()
smtp.Send(mm)
End Sub
雖然上述程式代碼相當冗長,但大部分程式代碼會建立 HTML,該 HTML 會顯示在傳送給開發人員的電子郵件中。 程式代碼一開始會參考 HttpException
方法所 GetLastError
傳回的 () lastErrorWrapper
。 透過 擷取 lastErrorWrapper.InnerException
要求所引發的實際例外狀況,並指派給變數 lastError
。 類型、訊息和堆疊追蹤資訊是從擷 lastError
取並儲存在三個字串變數中。
接下來,會 MailMessage
建立名為 mm
的物件。 電子郵件本文的格式為 HTML,並顯示所要求頁面的 URL、目前登入用戶的名稱,以及例外狀況的相關信息 (類型、訊息和堆疊追蹤) 。 類別的其中一個非經常性專案 HttpException
是您可以藉由呼叫 GetHtmlErrorMessage 方法來產生 HTML,以用來建立例外狀況詳細數據的黃色螢幕 (YSOD) 。 這個方法可用來擷取例外狀況詳細數據 YSOD 標記,並將其新增至電子郵件作為附件。 一個警告:如果觸發 Error
事件的例外狀況是 HTTP 型例外狀況, (例如要求不存在的頁面) 則 GetHtmlErrorMessage
方法會傳回 null
。
最後一個步驟是傳送 MailMessage
。 這是藉由建立新的 SmtpClient
方法並呼叫其 Send
方法來完成。
注意
在 Web 應用程式中使用此程式碼之前,您會想要將 和 FromAddress
常數中的ToAddress
值從 support@example.com 變更為錯誤通知電子郵件應該傳送至和源自的任何電子郵件位址。 您也必須在中的 <system.net>
Web.config
區段中指定 SMTP 伺服器設定。 請洽詢您的 Web 主機提供者,以判斷要使用的 SMTP 伺服器設定。
只要有錯誤,開發人員就會傳送電子郵件訊息,以摘要說明錯誤並包含 YSOD。 在上述教學課程中,我們示範了運行時間錯誤,方法是流覽Genre.aspx,並透過查詢字串傳入無效 ID
的值,例如 Genre.aspx?ID=foo
。 瀏覽檔案 Global.asax
所在的頁面會產生與上一個教學課程相同的用戶體驗 - 在開發環境中,您會繼續在開發環境中看到例外狀況詳細數據黃色畫面,而在生產環境中,您會看到自定義錯誤頁面。 除了這個現有的行為之外,開發人員還會傳送電子郵件。
圖 2 顯示造訪 Genre.aspx?ID=foo
時收到的電子郵件。 電子郵件本文摘要說明例外狀況資訊,而 YSOD.htm
附件會顯示例外狀況詳細數據 YSOD 中顯示的內容 (請參閱 圖 3) 。
圖 2:每當發生未處理的例外狀況時,開發人員就會傳送 Email 通知
(按兩下即可檢視完整大小的影像)
圖 3:Email 通知包含例外狀況詳細數據 YSOD 作為附件
(按兩下即可檢視完整大小的影像)
使用自訂錯誤頁面呢?
本教學課程示範如何使用 Global.asax
和 Application_Error
事件處理程式,在發生未處理的例外狀況時執行程序代碼。 具體而言,我們使用此事件處理程式來通知開發人員發生錯誤;我們可以擴充它,以同時記錄資料庫中的錯誤詳細數據。 事件處理程式的存在 Application_Error
不會影響終端用戶的體驗。 他們仍然會看到設定的錯誤頁面,可能是錯誤詳細數據 YSOD、運行時間錯誤 YSOD 或自定義錯誤頁面。
使用自定義錯誤頁面時,最好知道檔案和Application_Error
事件是否Global.asax
必要。 發生錯誤時,用戶會顯示自定義錯誤頁面,因此為何無法將程式代碼放入通知開發人員,並將錯誤詳細數據記錄到自定義錯誤頁面的程式代碼後置類別中? 雖然您可以確實將程式代碼新增至自定義錯誤頁面的程式代碼後置類別,但您無法存取在使用我們在上一個教學課程中探索的技術時觸發 Error
事件的例外狀況詳細數據。 GetLastError
從自訂錯誤頁面呼叫 方法會傳Nothing
回 。
此行為的原因是因為透過重新導向到達自定義錯誤頁面。 當未處理的例外狀況到達 ASP.NET 運行時間時,ASP.NET 引擎會引發其Error
事件 (,它會Application_Error
執行事件處理程式) ,然後發出 Response.Redirect(customErrorPageUrl)
,將使用者重新導向至自定義錯誤頁面。 方法 Response.Redirect
會以 HTTP 302 狀態代碼將回應傳送給用戶端,指示瀏覽器要求新的 URL,也就是自訂錯誤頁面。 瀏覽器接著會自動要求這個新頁面。 您可以告知自定義錯誤頁面是從產生錯誤的頁面分開要求,因為瀏覽器的網址列變更為自定義錯誤頁面 URL, (請參閱 圖 4) 。
圖 4:發生錯誤時,瀏覽器會重新導向至自定義錯誤頁面 URL
(按兩下即可檢視完整大小的影像)
淨效果是當伺服器以 HTTP 302 重新導向回應時,發生未處理的例外狀況的要求結束。 自定義錯誤頁面的後續要求是全新的要求;此時,ASP.NET 引擎已捨棄錯誤資訊,此外,也無法將先前要求中未處理的例外狀況與自定義錯誤頁面的新要求產生關聯。 這就是為什麼 GetLastError
從自定義錯誤頁面呼叫時傳 null
回的原因。
不過,在造成錯誤的相同要求期間,可能會執行自定義錯誤頁面。 方法會將 Server.Transfer(url)
執行傳送至指定的 URL,並在相同的要求中處理它。 您可以將事件處理程式中的 Application_Error
程式代碼移至自訂錯誤頁面的程式代碼後置類別,並將它 Global.asax
取代為下列程式代碼:
Sub Application_Error(ByVal sender As Object, ByVal e As EventArgs)
' Get the error details
Dim lastErrorWrapper As HttpException = _
CType(Server.GetLastError(), HttpException)
If lastErrorWrapper.GetHttpCode() = 404 Then
Server.Transfer("~/ErrorPages/404.aspx")
Else
Server.Transfer("~/ErrorPages/Oops.aspx")
End If
End Sub
現在當發生未處理的例外狀況時,事件處理程式會 Application_Error
根據 HTTP 狀態代碼,將控制權傳輸至適當的自定義錯誤頁面。 因為控制已傳輸,所以自定義錯誤頁面可以透過 存取 Server.GetLastError
未處理的例外狀況資訊,並可通知開發人員錯誤並記錄其詳細數據。 呼叫 Server.Transfer
會停止 ASP.NET 引擎,將使用者重新導向至自定義錯誤頁面。 相反地,自定義錯誤頁面的內容會當做產生錯誤的頁面回應傳回。
摘要
當 ASP.NET Web 應用程式中發生未處理的例外狀況時,ASP.NET 運行時間會引發 Error
事件並顯示已設定的錯誤頁面。 我們可以藉由建立 Error 事件的事件處理程式,通知開發人員錯誤、記錄其詳細數據,或以其他方式處理錯誤。 有兩種方式可以建立 HttpApplication
事件的事件處理程式,例如 Error
:在 Global.asax
檔案中,或從 HTTP 模組建立事件處理程式。 本教學課程示範如何在檔案中Global.asax
建立Error
事件處理程式,以透過電子郵件訊息通知開發人員錯誤。
Error
如果您需要以某種唯一或自定義的方式處理未處理的例外狀況,建立事件處理程式會很有用。 不過,建立您自己的 Error
事件處理程式來記錄例外狀況,或通知開發人員不是最有效率的時間使用時間,因為已有可用且容易使用的錯誤記錄連結庫,可在幾分鐘內進行設定。 接下來的兩個教學課程會檢查兩個這類連結庫。
快樂的程序設計!
深入閱讀
如需本教學課程中所討論之主題的詳細資訊,請參閱下列資源: