未處理的例外會顯示「黃色死機畫面」(YSOD)或 IIS 預設錯誤頁,嚇跑使用者、洩露堆疊資訊、破壞信任。ASP.NET 提供四層錯誤處理機制,依範圍從廣到窄:
web.config <customErrors>、Global.asax Application_ErrorPage.ErrorPage 屬性、Page_Error 事件try/catch/finally、using 區塊IHttpModule、ASP.NET Core 中介軟體(現代架構)<system.web>
<customErrors mode="On" defaultRedirect="~/Error/GenericError.aspx">
<error statusCode="404" redirect="~/Error/404.aspx" />
<error statusCode="500" redirect="~/Error/500.aspx" />
</customErrors>
</system.web>
<!-- IIS 7+ 整合管線建議使用 httpErrors -->
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<remove statusCode="500" />
<error statusCode="404" path="/Error/404" responseMode="ExecuteURL" />
<error statusCode="500" path="/Error/500" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
protected void Application_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError().GetBaseException();
// 記錄完整例外(含堆疊、內部例外、請求資訊)
LogException(ex, Request.Url, Request.UserHostAddress);
// 清除錯誤,避免觸發 customErrors 重導向
Server.ClearError();
// 可選:轉向自訂錯誤頁並傳遞例外 ID
Response.Redirect("~/Error/GenericError.aspx?exId=" + Guid.NewGuid());
}
Server.ClearError() 必須呼叫,否則 ASP.NET 仍會依 customErrors 重導向,導致雙重處理。<%@ Page Language="C#" ErrorPage="~/Error/PageSpecificError.aspx" %>
protected void Page_Error(object sender, EventArgs e)
{
Exception ex = Server.GetLastError();
if (ex is SqlException)
{
// 特定處理:記錄、顯示友善訊息、或轉向特定頁面
Server.Transfer("~/Error/DbError.aspx");
}
// 不 ClearError,讓應用程式層再處理或顯示 customErrors
}
Server.Transfer 保留原始 URL 與例外上下文。try
{
using (SqlConnection conn = new SqlConnection(connStr))
{
conn.Open();
// 執行命令
}
}
catch (SqlException ex) when (ex.Number == 2627) // 唯一鍵衝突
{
ModelState.AddModelError("", "資料重複,請修改後重試。");
}
catch (Exception ex)
{
_logger.Error(ex);
throw; // 重拋讓上層處理
}
原則:只捕獲你知道如何處理的例外;其餘向上傳播。
try/catch → 若捕獲並處理(不重拋),流程結束。Page_Error → 若 Server.ClearError() 或 Server.Transfer,流程結束。Application_Error → 若 Server.ClearError() + Response.Redirect,流程結束。customErrors / httpErrors → 依狀態碼重導向靜態/動態錯誤頁。// 1. 統一例外記錄介面
public interface IExceptionLogger { void Log(Exception ex, HttpRequestBase request); }
// 2. 全域錯誤處理模組 (HttpModule) - 取代 Global.asax,更模組化
public class ErrorHandlingModule : IHttpModule
{
private readonly IExceptionLogger _logger;
public void Init(HttpApplication context)
{
context.Error += (s, e) => {
var ex = context.Server.GetLastError().GetBaseException();
_logger.Log(ex, new HttpRequestWrapper(context.Request));
// 可選:context.Server.ClearError(); context.Response.Redirect(...);
};
}
}
// 3. 友善錯誤頁面控制器
public class ErrorController : Controller
{
public ActionResult Index(int? statusCode, string exId) { /* 顯示對應視圖 */ }
public ActionResult DbError() { return View(); }
public ActionResult NotFound() { Response.StatusCode = 404; return View(); }
}
customErrors mode="RemoteOnly" 讓本機開發者看詳細錯誤,遠端使用者看友善頁面。| 機制 | 範圍 | 能否記錄例外 | 能否依例外型別處理 | 推薦用途 |
|---|---|---|---|---|
| try/catch | 區塊 | 是 | 是 | 業務邏輯預期錯誤(驗證失敗、資料衝突) |
| Page_Error | 頁面 | 是 | 是 | 頁面特定錯誤轉向、保留上下文 |
| Application_Error | 應用程式 | 是 | 是 | 集中日誌、通知、安全稽核 |
| customErrors/httpErrors | 應用程式/IIS | 否 | 否(僅狀態碼) | 最後防線、靜態檔案錯誤、無程式碼部署 |
組合使用四層防護,依據錯誤性質在適當層級處理,才能打造穩健、安全、使用者友善的 ASP.NET 應用程式。