protected void Application_Error(object sender, EventArgs e)
{
var ex = Server.GetLastError().GetBaseException();
var msg = $"{DateTime.Now}: {Request.Url}\n{ex.Message}\n{ex.StackTrace}";
LogToFile(msg); // 自訂寫入檔案/資料庫/ELMAH/Serilog
Server.ClearError();
Response.Redirect("/Error/Index"); // 導向友善錯誤頁
}
優點:攔截所有未處理異常;缺點:難以依異常類型差異化處理、不支援 AJAX 請求回傳 JSON。
public class BaseController : Controller
{
protected override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled) return;
LogException(filterContext.Exception);
filterContext.ExceptionHandled = true;
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
filterContext.Result = Json(new { success = false, message = "伺服器錯誤" });
}
else
{
filterContext.Result = View("Error");
}
}
}
優點:可在基類統一處理、支援 AJAX/一般請求區分;缺點:需所有控制器繼承基類。
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)]
public class CustomHandleErrorAttribute : HandleErrorAttribute
{
public override void OnException(ExceptionContext filterContext)
{
if (filterContext.ExceptionHandled || !filterContext.HttpContext.IsCustomErrorEnabled)
return;
var ex = filterContext.Exception;
LogException(ex); // 整合 Serilog/NLog/ELMAH
if (new HttpRequestWrapper(filterContext.HttpContext.Request).IsAjaxRequest())
{
filterContext.Result = new JsonResult
{
Data = new { error = ex.Message },
JsonRequestBehavior = JsonRequestBehavior.AllowGet
};
}
else
{
var viewName = View ?? "Error";
filterContext.Result = new ViewResult
{
ViewName = viewName,
ViewData = new ViewDataDictionary<HandleErrorInfo>(new HandleErrorInfo(ex, ControllerName, ActionName))
};
}
filterContext.ExceptionHandled = true;
}
}
public class FilterConfig
{
public static void RegisterGlobalFilters(GlobalFilterCollection filters)
{
filters.Add(new CustomHandleErrorAttribute());
// filters.Add(new HandleErrorAttribute()); // 內建簡易版
}
}
web.config 設定 <customErrors mode="On" defaultRedirect="/Error">。404、500 等狀態碼顯示專屬視圖。