處理未處理的例外狀況 (C#)
檢視或下載範例程式碼 \(英文\) (如何下載)
當生產環境中 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.cs
。 檔案 Global.asax
只包含 @Application
參考 .cs
檔案的 指示詞;感興趣的事件處理程式定義於 檔案中 Global.asax.cs
。 若為 WSP,只會建立單一檔案, Global.asax
而且事件處理程式會在 區塊中 <script runat="server">
定義。
Global.asax
由 Visual Studio 的全域應用程式類別範本在 WAP 中建立的檔案包含名為 Application_BeginRequest
、 Application_AuthenticateRequest
和 的事件處理程式,分別是事件BeginRequest
、 AuthenticateRequest
和 Application_Error
的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.cs
在 WAP 中建立的檔案不需要複製到生產環境,因為此程式代碼會編譯成專案的元件。
Visual Studio 的全域應用程式類別範本所建立的事件處理程式並不詳盡。 您可以藉由命名事件處理程式 ,為任何 HttpApplication
事件新增事件處理程式 Application_EventName
。 例如,您可以將下列程式代碼新增至 Global.asax
檔案,以建立 AuthorizeRequest
事件的事件處理程式:
protected void Application_AuthorizeRequest(object sender, EventArgs e)
{
// Event handler code
}
同樣地,您可以移除不需要的全域應用程式類別範本所建立的任何事件處理程式。 在本教學課程中,我們只需要事件的事件處理程式 Error
;您可以隨意從 Global.asax
檔案中移除其他事件處理程式。
注意
HTTP 模組 提供另一種方式來定義事件的事件處理程式 HttpApplication
。 HTTP 模組會建立為類別檔案,可以直接放在 Web 應用程式專案內,或分隔成個別的類別庫。 因為您可以將它們分成類別庫,所以 HTTP 模組提供更有彈性且可重複使用的模型來建立 HttpApplication
事件處理程式。 雖然檔案 Global.asax
專屬於其所在 Web 應用程式,但 HTTP 模組可以編譯成元件,此時將 HTTP 模組新增至網站就像將 Bin
元件卸除至資料夾中,並在中 Web.config
註冊 Module 一樣簡單。 本教學課程不會探討如何建立和使用 HTTP 模組,但下列兩個教學課程中使用的兩個錯誤記錄連結庫會實作為 HTTP 模組。 如需 HTTP 模組優點的詳細資訊,請參閱 使用 HTTP 模組和處理程式建立可插式 ASP.NET 元件。
擷取未處理例外狀況的相關信息
此時,我們有具有事件處理程式的 Application_Error
Global.asax 檔案。 當此事件處理程式執行時,我們需要通知開發人員錯誤並記錄其詳細數據。 若要完成這些工作,我們必須先判斷所引發例外狀況的詳細數據。 使用 Server 物件的 GetLastError
方法來 擷取造成事件引發 Error
之未處理例外狀況的詳細數據。
protected void Application_Error(object sender, EventArgs e)
{
// Get the error details
HttpException lastErrorWrapper =
Server.GetLastError() as HttpException;
}
方法 GetLastError
會傳回 類型的 Exception
物件,這是 .NET Framework 中所有例外狀況的基底類型。 不過,在上述程序代碼中,我正在將 所傳GetLastError
HttpException
回的Exception物件轉換成物件。 Error
如果因為處理 ASP.NET 資源期間擲回例外狀況而引發事件,則擲回的例外狀況會包裝在 內HttpException
。 若要取得產生 Error 事件的實際例外狀況,請使用 InnerException
屬性。 Error
如果事件因為 HTTP 型例外狀況而引發,例如要求不存在的頁面,則會擲回 ,HttpException
但沒有內部例外狀況。
下列程式代碼會使用 GetLastErrormessage
來擷取觸發 Error
事件之例外狀況的相關信息,並將 HttpException
儲存在名為 lastErrorWrapper
的變數中。 然後,它會將原始例外狀況的類型、訊息和堆疊追蹤儲存在三個字元串變數中,檢查 是否 lastErrorWrapper
為觸發 Error
事件的實際例外狀況(在 HTTP 型例外狀況的情況下),或者它是否只是處理要求時擲回之例外狀況的包裝函式。
protected void Application_Error(object sender, EventArgs e)
{
// Get the error details
HttpException lastErrorWrapper =
Server.GetLastError() as HttpException;
Exception lastError = lastErrorWrapper;
if (lastErrorWrapper.InnerException != null)
lastError = lastErrorWrapper.InnerException;
string lastErrorTypeName = lastError.GetType().ToString();
string lastErrorMessage = lastError.Message;
string lastErrorStackTrace = lastError.StackTrace;
}
此時,您擁有將例外狀況詳細數據記錄至資料庫數據表所需的所有資訊。 您可以針對感興趣的每個錯誤詳細數據建立資料庫數據表,包括類型、訊息、堆疊追蹤等等,以及其他有用的資訊片段,例如所要求頁面的 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 網頁網站傳送電子郵件,以及 System.Net.Mail。
注意
元素 <system.net>
包含傳送電子郵件時 類別 SmtpClient
所使用的 SMTP 伺服器設定。 您的 Web 主控公司可能會有 SMTP 伺服器,可用來從應用程式傳送電子郵件。 如需您應該在 Web 應用程式中使用的 SMTP 伺服器設定資訊,請參閱 Web 主機的支援區段。
將下列程式代碼新增至事件處理程式, Application_Error
以在發生錯誤時傳送電子郵件給開發人員:
void Application_Error(object sender, EventArgs e)
{
// Get the error details
HttpException lastErrorWrapper =
Server.GetLastError() as HttpException;
Exception lastError = lastErrorWrapper;
if (lastErrorWrapper.InnerException != null)
lastError = lastErrorWrapper.InnerException;
string lastErrorTypeName = lastError.GetType().ToString();
string lastErrorMessage = lastError.Message;
string lastErrorStackTrace = lastError.StackTrace;
const string ToAddress = "support@example.com";
const string FromAddress = "support@example.com";
const string Subject = "An Error Has Occurred!";
// Create the MailMessage object
MailMessage mm = new MailMessage(FromAddress, ToAddress);
mm.Subject = Subject;
mm.IsBodyHtml = true;
mm.Priority = MailPriority.High;
mm.Body = string.Format(@"
<html>
<body>
<h1>An Error Has Occurred!</h1>
<table cellpadding=""5"" cellspacing=""0"" border=""1"">
<tr>
<tdtext-align: right;font-weight: bold"">URL:</td>
<td>{0}</td>
</tr>
<tr>
<tdtext-align: right;font-weight: bold"">User:</td>
<td>{1}</td>
</tr>
<tr>
<tdtext-align: right;font-weight: bold"">Exception Type:</td>
<td>{2}</td>
</tr>
<tr>
<tdtext-align: right;font-weight: bold"">Message:</td>
<td>{3}</td>
</tr>
<tr>
<tdtext-align: right;font-weight: bold"">Stack Trace:</td>
<td>{4}</td>
</tr>
</table>
</body>
</html>",
Request.RawUrl,
User.Identity.Name,
lastErrorTypeName,
lastErrorMessage,
lastErrorStackTrace.Replace(Environment.NewLine, "<br />"));
// Attach the Yellow Screen of Death for this error
string YSODmarkup = lastErrorWrapper.GetHtmlErrorMessage();
if (!string.IsNullOrEmpty(YSODmarkup))
{
Attachment YSOD =
Attachment.CreateAttachmentFromString(YSODmarkup, "YSOD.htm");
mm.Attachments.Add(YSOD);
}
// Send the email
SmtpClient smtp = new SmtpClient();
smtp.Send(mm);
}
雖然上述程式代碼相當冗長,但大部分程式代碼會建立 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 並透過querystring傳入無效 ID
的值,例如 Genre.aspx?ID=foo
,示範運行時錯誤。 瀏覽檔案所在的頁面 Global.asax
會產生與上一個教學課程相同的用戶體驗 - 在開發環境中,您會繼續看到例外狀況詳細數據黃色死亡畫面,而在實際執行環境中,您會看到自定義錯誤頁面。 除了此現有的行為之外,開發人員也會傳送電子郵件。
圖 2 顯示造訪 Genre.aspx?ID=foo
時收到的電子郵件。 電子郵件本文摘要說明例外狀況資訊,而 YSOD.htm
附件會顯示例外狀況詳細數據 YSOD 中顯示的內容(請參閱 圖 3)。
圖 2:每當發生未處理的例外狀況時,開發人員就會傳送電子郵件通知
(點擊查看完整圖片)
圖 3:電子郵件通知包含例外狀況詳細數據 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 引擎已捨棄錯誤資訊,此外,也無法將先前要求中未處理的例外狀況與自定義錯誤頁面的新要求產生關聯。 這就是為什麼從自定義錯誤頁面呼叫時傳回null
的原因GetLastError
。
不過,在造成錯誤的相同要求期間,可以執行自定義錯誤頁面。 方法會將 Server.Transfer(url)
執行傳送至指定的 URL,並在相同的要求內處理它。 您可以將事件處理程式中的 Application_Error
程式代碼移至自訂錯誤頁面的程式代碼後置類別,並將它 Global.asax
取代為下列程式代碼:
protected void Application_Error(object sender, EventArgs e)
{
// Transfer the user to the appropriate custom error page
HttpException lastErrorWrapper =
Server.GetLastError() as HttpException;
if (lastErrorWrapper.GetHttpCode() == 404)
{
Server.Transfer("~/ErrorPages/404.aspx");
}
else
{
Server.Transfer("~/ErrorPages/Oops.aspx");
}
}
現在,當未處理的例外狀況發生時, 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
事件處理程式來記錄例外狀況或通知開發人員並不是您時間最有效率的使用方式,因為已經有可用且容易使用的錯誤記錄連結庫,可以在幾分鐘內進行設定。 接下來的兩個教學課程會檢查兩個這類連結庫。
快樂程式!
深入閱讀
有關本教學課程中討論的主題的更多資訊,請參閱以下資源: