共用方式為


處理未處理的例外狀況 (VB)

作者 :Scott Mitchell

檢視或下載範例程式碼 \(英文\) (如何下載)

當生產環境中 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 管線的特定階段引發。 例如,HttpApplicationBeginRequest類別的事件會在每個要求開始時引發;當安全性模塊識別出要求者時,就會引發其 AuthenticateRequest 事件。 這些 HttpApplication 事件可讓頁面開發人員在要求存留期的各個點上執行自定義邏輯。

事件的事件處理程式 HttpApplication 可以放在名為 Global.asax的特殊檔案中。 若要在網站中建立此檔案,請使用名為的全域應用程式類別範本 Global.asax,將新專案新增至網站的根目錄。

使用全域應用程式類別範本將新專案新增至網站的根目錄,其名稱為 Global.asax,以在您的網站中建立此檔案的螢幕快照。

圖 1:新增 Global.asax 至 Web 應用程式
(按兩下即可檢視完整大小的影像)

Visual Studio 所建立檔案的內容和結構 Global.asax 會根據您使用 Web 應用程式專案 (WAP) 或網站專案 (WSP) 而略有不同。 使用 WAP 時,會 Global.asax 實作為兩個不同的檔案 - Global.asaxGlobal.asax.vb。 檔案Global.asax只包含參考.vb檔案的指示詞;感興趣的事件處理程式定義於 檔案中Global.asax.vb@Application。 對於 WSP,只會建立單一檔案, Global.asax而且事件處理程式會在 區塊中 <script runat="server"> 定義。

Global.asax由 Visual Studio 的全域應用程式類別範本在 WAP 中建立的檔案包含名為 Application_BeginRequestApplication_AuthenticateRequestApplication_Error的事件處理程式,分別是事件BeginRequestAuthenticateRequestError的事件處理程式HttpApplication。 另外還有名為 Application_StartSession_StartApplication_EndSession_End的事件處理程式,這些事件處理程式會在 Web 應用程式啟動時、新工作階段啟動時、應用程式結束時,以及工作階段分別結束時引發的事件處理程式。 Global.asax Visual Studio 在 WSP 中建立的檔案只Application_Error包含 、Application_StartSession_StartApplication_EndSession_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 中所有例外狀況的基底類型。 不過,在上述程序代碼中,我正在將 所GetLastErrorHttpException傳回的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代表電子郵件訊息,並具有、FromSubjectBody、 和 AttachmentsTo屬性。 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.asaxApplication_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) 。

發生錯誤時,重新導向至自定義錯誤頁面 U R L 之瀏覽器的螢幕快照。

圖 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 事件處理程式來記錄例外狀況,或通知開發人員不是最有效率的時間使用時間,因為已有可用且容易使用的錯誤記錄連結庫,可在幾分鐘內進行設定。 接下來的兩個教學課程會檢查兩個這類連結庫。

快樂的程序設計!

深入閱讀

如需本教學課程中所討論之主題的詳細資訊,請參閱下列資源: