透過自訂 ExtendedController 基類,覆寫 OnException 整合 Enterprise Library Exception Handling Application Block (EHAB),實現聲明式異常處理策略。
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class ExceptionPolicyAttribute : Attribute
{
public string ExceptionPolicyName { get; }
public ExceptionPolicyAttribute(string policyName) { ExceptionPolicyName = policyName; }
}
[AttributeUsage(AttributeTargets.Method | AttributeTargets.Class)]
public class HandleErrorActionAttribute : Attribute
{
public string HandleErrorAction { get; }
public HandleErrorActionAttribute(string actionName) { HandleErrorAction = actionName; }
}
public class ExtendedController : Controller
{
private static Dictionary<Type, ControllerDescriptor> _controllerDescriptors = new Dictionary<Type, ControllerDescriptor>();
private static object _sync = new object();
// 快取 ControllerDescriptor
public ControllerDescriptor Descriptor
{
get
{
Type type = GetType();
if (_controllerDescriptors.TryGetValue(type, out var desc)) return desc;
lock (_sync)
{
if (_controllerDescriptors.TryGetValue(type, out desc)) return desc;
desc = new ReflectedControllerDescriptor(type);
_controllerDescriptors[type] = desc;
return desc;
}
}
}
// 取得異常處理策略名稱 (Action > Controller > 預設空字串)
public string GetExceptionPolicyName()
{
string actionName = ControllerContext.RouteData.GetRequiredString("action");
var actionDesc = Descriptor.FindAction(ControllerContext, actionName);
if (actionDesc == null) return String.Empty;
return actionDesc.GetCustomAttributes(true).OfType<ExceptionPolicyAttribute>().FirstOrDefault()?.ExceptionPolicyName
?? Descriptor.GetCustomAttributes(true).OfType<ExceptionPolicyAttribute>().FirstOrDefault()?.ExceptionPolicyName
?? String.Empty;
}
// 取得錯誤處理 Action 名稱
public string GetHandleErrorActionName()
{
string actionName = ControllerContext.RouteData.GetRequiredString("action");
var actionDesc = Descriptor.FindAction(ControllerContext, actionName);
if (actionDesc == null) return String.Empty;
return actionDesc.GetCustomAttributes(true).OfType<HandleErrorActionAttribute>().FirstOrDefault()?.HandleErrorAction
?? Descriptor.GetCustomAttributes(true).OfType<HandleErrorActionAttribute>().FirstOrDefault()?.HandleErrorAction
?? String.Empty;
}
// 專用 ActionInvoker 執行錯誤 Action
public HandleErrorActionInvoker HandleErrorActionInvoker { get; private set; }
public ExtendedController() { HandleErrorActionInvoker = new HandleErrorActionInvoker(); }
// 核心異常處理邏輯
protected override void OnException(ExceptionContext filterContext)
{
// 1. 取得策略名稱
string policyName = GetExceptionPolicyName();
if (String.IsNullOrEmpty(policyName)) return; // 無策略則不處理
// 2. 執行 EHAB 處理
var ex = filterContext.Exception;
var handled = ExceptionPolicy.HandleException(ex, policyName); // EntLib 核心呼叫
// 3. 根據請求類型回應
if (filterContext.HttpContext.Request.IsAjaxRequest())
{
// AJAX: 回傳 JSON 異常資訊
var errorData = new { Message = ex.Message, StackTrace = ex.StackTrace };
filterContext.Result = new JsonResult { Data = errorData, JsonRequestBehavior = JsonRequestBehavior.AllowGet };
}
else
{
// 一般請求: 嘗試執行 HandleErrorAction
string errorAction = GetHandleErrorActionName();
if (!String.IsNullOrEmpty(errorAction))
{
var errorActionDesc = Descriptor.FindAction(ControllerContext, errorAction);
if (errorActionDesc != null)
{
filterContext.Result = HandleErrorActionInvoker.InvokeAction(ControllerContext, errorActionDesc);
filterContext.ExceptionHandled = true;
return;
}
}
// 找不到則顯示預設錯誤 View
filterContext.Result = View("Error");
}
filterContext.ExceptionHandled = true;
}
}
public class HandleErrorActionInvoker : ControllerActionInvoker
{
protected override ActionResult InvokeAction(ControllerContext controllerContext, string actionName)
{
var controllerDesc = new ReflectedControllerDescriptor(controllerContext.Controller.GetType());
var actionDesc = controllerDesc.FindAction(controllerContext, actionName);
return InvokeAction(controllerContext, actionDesc);
}
}
<configSections>
<section name="exceptionHandling" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Configuration.ExceptionHandlingSettings, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling" />
</configSections>
<exceptionHandling>
<exceptionPolicies>
<add name="defaultPolicy">
<exceptionTypes>
<add name="All Exceptions" type="System.Exception" postHandlingAction="NotifyRethrow">
<exceptionHandlers>
<add name="Logging Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging.LoggingExceptionHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.Logging"
logCategory="Exception" eventId="100" severity="Error" title="Enterprise Library Exception Handling"
formatterType="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.TextExceptionFormatter, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
priority="0" />
<add name="Wrap Handler" type="Microsoft.Practices.EnterpriseLibrary.ExceptionHandling.WrapHandler, Microsoft.Practices.EnterpriseLibrary.ExceptionHandling"
wrapExceptionType="System.ApplicationException, mscorlib" wrapExceptionMessage="系統錯誤" />
</exceptionHandlers>
</add>
</exceptionTypes>
</add>
</exceptionPolicies>
</exceptionHandling>
[ExceptionPolicy("defaultPolicy")]
public class HomeController : ExtendedController
{
[HttpPost]
[HandleErrorAction("OnIndexError")]
public ActionResult Index(LoginInfo loginInfo)
{
if (loginInfo.UserName != "foo") throw new InvalidUserNameException();
if (loginInfo.Password != "password") throw new InvalidPasswordException();
return View(loginInfo);
}
public ActionResult OnIndexError(LoginInfo loginInfo) => View(loginInfo);
}
透過 ExtendedController.OnException 整合 EntLib EHAB,配合 ExceptionPolicyAttribute (策略) 與 HandleErrorActionAttribute (錯誤頁面 Action),實現聲明式、可配置、情境化的自動化異常處理。AJAX 自動回傳 JSON,一般請求優先執行指定錯誤 Action,最後回退預設錯誤 View。