前兩講建立了日誌記錄與例外捕獲機制,但仍有「漏網之魚」——未被 try-catch 捕獲的例外、404 找不到頁面、以及開發者疏忽遺漏的錯誤。本篇實作 Global.asax Application_Error 全域例外處理機制,統一攔截所有未處理例外,記錄詳細錯誤資訊,並友善導向自訂錯誤頁面,避免暴露敏感堆疊追蹤給使用者。
try-catch 樣板代碼(業務邏輯層仍建議保留)protected void Application_Error(object sender, EventArgs e) {
var context = HttpContext.Current;
var requestUrl = context.Request.Url.ToString();
var server = context.Server;
// 取得最後一個未處理的例外
Exception lastError = server.GetLastError();
if (lastError == null) return;
// 1. 記錄例外到日誌系統(資料庫/檔案/第三方如 Elmah、Serilog)
ExceptionHander.WriteException(lastError);
// 2. 暫存例外供錯誤頁面顯示詳細資訊(開發環境)
context.Application["LastError"] = lastError;
// 3. 取得 HTTP 狀態碼
int statusCode = context.Response.StatusCode;
// 4. 導向自訂錯誤頁面
const string errorHandlerPath = "/SysException/Error";
try {
if (!string.IsNullOrEmpty(errorHandlerPath)) {
// 解析虛擬路徑
string resolvedUrl = new System.Web.UI.Control().ResolveUrl(errorHandlerPath);
string redirectUrl = string.Format("{0}?ErrorUrl={1}", resolvedUrl, server.UrlEncode(requestUrl));
// 使用 JavaScript 重導向(避免 Response.Redirect 在錯誤處理中拋出 ThreadAbortException)
string script = string.Format(
"<script language='javascript' type='text/javascript'>window.top.location='{0}';</script>",
redirectUrl
);
context.Response.Clear();
context.Response.Write(script);
context.Response.End();
}
} catch (Exception redirectEx) {
// 導向失敗時的最後防線:直接輸出簡易錯誤訊息
context.Response.Clear();
context.Response.Write("系統發生錯誤,請聯繫管理員。");
context.Response.End();
}
}
public class SysExceptionController : Controller {
public ActionResult Error(string errorUrl) {
var lastError = HttpContext.Application["LastError"] as Exception;
var viewModel = new ErrorViewModel {
ErrorUrl = errorUrl,
ErrorMessage = lastError?.Message,
StackTrace = lastError?.StackTrace,
Source = lastError?.Source,
TargetSite = lastError?.TargetSite?.ToString(),
Timestamp = DateTime.Now,
// 開發環境顯示詳細資訊,正式環境僅顯示友善訊息
ShowDetails = HttpContext.IsDebuggingEnabled
};
// 記錄存取錯誤頁面本身的請求
LogHelper.Info($"錯誤頁面存取: {errorUrl}, 例外: {lastError?.Message}");
Response.StatusCode = 500;
return View("Error", viewModel);
}
public ActionResult NotFound() {
Response.StatusCode = 404;
return View("NotFound");
}
}
@model ErrorViewModel
@{
ViewBag.Title = "系統錯誤";
Layout = "~/Views/Shared/_ErrorLayout.cshtml";
}
<div class="error-container">
<div class="error-icon">⚠</div>
<h1>系統發生錯誤</h1>
<p class="error-message">很抱歉,系統在處理您的請求時發生了非預期的錯誤。</p>
@if (Model.ShowDetails) {
<div class="error-details">
<h3>詳細資訊(僅開發環境顯示)</h3>
<p><strong>請求網址:</strong> @Model.ErrorUrl</p>
<p><strong>錯誤訊息:</strong> @Model.ErrorMessage</p>
<p><strong>來源:</strong> @Model.Source</p>
<p><strong>方法:</strong> @Model.TargetSite</p>
<pre>@Model.StackTrace</pre>
</div>
}
<div class="error-actions">
<a href="javascript:history.back()" class="btn btn-primary">返回上一頁</a>
<a href="@Url.Action("Index", "Home")" class="btn btn-default">回首頁</a>
<a href="mailto:support@company.com?subject=系統錯誤回報&body=@Model.ErrorUrl" class="btn btn-link">回報問題</a>
</div>
</div>
在任意 Action 中植入測試代碼:
public ActionResult TestError() {
// 模擬未處理例外
int.Parse("這不是數字"); // FormatException
return View();
}
瀏覽該頁面將觸發 Application_Error,導向自訂錯誤頁面並記錄完整堆疊追蹤。

ExceptionHander,獲得儀表板、郵件通知、錯誤分組等企業級功能HttpException 404 導向 NotFound 頁面,HttpException 403 導向 AccessDenied 頁面/health 端點供負載平衡器監控,例外率過高時自動摘除節點CorrelationId,貫穿日誌、錯誤頁面、用戶回報,便於端到端追蹤<system.web>
<customErrors mode="RemoteOnly" defaultRedirect="/SysException/Error">
<error statusCode="404" redirect="/SysException/NotFound" />
<error statusCode="500" redirect="/SysException/Error" />
</customErrors>
</system.web>
<system.webServer>
<httpErrors errorMode="Custom" existingResponse="Replace">
<remove statusCode="404" />
<remove statusCode="500" />
<error statusCode="404" path="/SysException/NotFound" responseMode="ExecuteURL" />
<error statusCode="500" path="/SysException/Error" responseMode="ExecuteURL" />
</httpErrors>
</system.webServer>
全域例外處理是系統穩健性的最後一道防線,配合結構化日誌與監控告警,能將「被動修補」轉變為「主動預防」,大幅提升系統可靠性與維運效率。