ASP.NET MVC集成EntLib异常处理实现

  通过《ASP.NET MVC集成EntLib异常处理实例》的实演示可以看出我们通过扩展实现的自动异常处理机制能够利用EntLib的EHAB根据执行的一场处理策略对某个Action方法执行过程中抛出的异常进行处理。对于处理后的结果,则按照如下的机制对请求进行响应。[源代码从这里下载]

  • 对于Ajax请求,直接创建一个用于封装被处理后异常的数据对象,并据此创建一个JsonResult将异常信息回复给客户端。
  • 对于非Ajax请求,如果当前Action方法上应用HandleErrorActionAttribute特性设置了匹配的Action方法用于处理该方法抛出的异常,那么执行该方法并用返回的ActionResult对象响应当前请求。
  • 如果HandleErrorActionAttribute特性不曾应用在当前Action方法上,或者通过该特性指定的Action不存在,则将默认的错误View呈现出来作为多请求的响应。

一、ExceptionPolicyAttribute & HandleErrorActionAttribute

  所有的这些都是通过一个自定义的ExceptionFilter来实现的。不过我们并没有定义任何的ExceptionFilter特性,而是将异常处理实 现在一个自定义的ExtendedController基类中,对异常的自动处理实现在重写的OnException方法中,不过在介绍该方法的逻辑之前 我们先来看看定义在ExtendedController中的其他辅助成员。

   1: public class ExtendedController: Controller

   2: {

   3:     private static Dictionary<Type, ControllerDescriptor> controllerDescriptors = new Dictionary<Type, ControllerDescriptor>();

   4:     private static object syncHelper = new object();

   5:  

   6:     protected override void OnException(ExceptionContext filterContext)

   7:     {

   8:         //省略成员

   9:     }      

  10:     

  11:     //描述当前Controller的ControllerDescriptor

  12:     public ControllerDescriptor Descriptor

  13:     {

  14:         get

  15:         {

  16:             ControllerDescriptor descriptor;

  17:             if(controllerDescriptors.TryGetValue(this.GetType(), out descriptor))

  18:             {

  19:                 return descriptor;

  20:             }

  21:             lock (syncHelper)

  22:             {

  23:                 if (controllerDescriptors.TryGetValue(this.GetType(), out descriptor))

  24:                 {

  25:                     return descriptor;

  26:                 }

  27:                 else

  28:                 {

  29:                     descriptor = new ReflectedControllerDescriptor(this.GetType());

  30:                     controllerDescriptors.Add(this.GetType(), descriptor);

  31:                     return descriptor;

  32:                 }

  33:             }

  34:         }

  35:     }

  36:     //获取异常处理策略名称

  37:     public string GetExceptionPolicyName()

  38:     {

  39:         string actionName = ControllerContext.RouteData.GetRequiredString("action");

  40:         ActionDescriptor actionDescriptor = this.Descriptor.FindAction(ControllerContext, actionName);

  41:         if (null == actionDescriptor)

  42:         {

  43:             return string.Empty;

  44:         }

  45:         ExceptionPolicyAttribute exceptionPolicyAttribute = actionDescriptor.GetCustomAttributes(true).OfType<ExceptionPolicyAttribute>().FirstOrDefault()??               

  46:            Descriptor.GetCustomAttributes(true).OfType<ExceptionPolicyAttribute>().FirstOrDefault()?? new ExceptionPolicyAttribute("");

  47:         return exceptionPolicyAttribute.ExceptionPolicyName;

  48:     }

  49:  

  50:     //获取Handle-Error-Action名称

  51:     public string GetHandleErrorActionName()

  52:     {

  53:         string actionName = ControllerContext.RouteData.GetRequiredString("action");

  54:         ActionDescriptor actionDescriptor = this.Descriptor.FindAction(ControllerContext, actionName);

  55:         if (null == actionDescriptor)

  56:         {

  57:             return string.Empty;

  58:         }

  59:         HandleErrorActionAttribute handleErrorActionAttribute = actionDescriptor.GetCustomAttributes(true).OfType<HandleErrorActionAttribute>().FirstOrDefault()??          

  60:             Descriptor.GetCustomAttributes(true).OfType<HandleErrorActionAttribute>().FirstOrDefault()?? new HandleErrorActionAttribute("");

  61:         return handleErrorActionAttribute.HandleErrorAction;

  62:     }

  63:  

  64:     //用于执行Handle-Error-Action的ActionInvoker

  65:     public HandleErrorActionInvoker HandleErrorActionInvoker { get; private set; }

  66:  

  67:     public ExtendedController()

  68:     {

  69:         this.HandleErrorActionInvoker = new HandleErrorActionInvoker();

  70:     }

  71: }

  ExtendedController的Descriptor属性用于返回描述自身的ControllerDescriptor对象,实际上是一个 ReflectedControllerDescriptor对象。为了避免频繁的反射操作造成对性能的影响,我们将基于某个类型解析出来的 ReflectedControllerDescriptor对象进行了全局性缓存。

  GetExceptionPolicyName方法用于返回当前采用的异常处理策略名称。异常处理策略名称是通过具有如下定义的 ExceptionPolicyAttribute特性来指定的。该特性既可以应用在Controller类型上,也可以应用在Action方法上,换句 话说,我们可以采用不同的策略来处理从不同Action执行过程中抛出的异常。GetExceptionPolicyName利用 ControllerDesctior和ActionDescriptor可以很容易地得到应用的ExceptionPolicyAttribute特 性,进而得到相应的异常处理策略名称。

   1: [AttributeUsage( AttributeTargets.Class| AttributeTargets.Method, AllowMultiple = false, Inherited = true)]

   2: public class ExceptionPolicyAttribute: Attribute

   3: {

   4:     public string ExceptionPolicyName { get; private set; }

   5:     public ExceptionPolicyAttribute(string exceptionPolicyName)

   6:     {

   7:         this.ExceptionPolicyName = exceptionPolicyName;

   8:     }

   9: }

  另一个方法GetHandleErrorActionName用于获取通过应用在Action方法上的特性 HandleErrorActionAttribute设置的Handle-Error-Action的名称。该特性定义如下,它既可以应用于某个 Action方法,也可以应用于Controller类。GetHandleErrorActionName方法同样利用 ControllerDesctior和ActionDescriptor得到应用的ExceptionPolicyAttribute特性,并最终得到 对应的异常处理Action名称。

   1: [AttributeUsage( AttributeTargets.Class| AttributeTargets.Method, AllowMultiple = false)]

   2: public class HandleErrorActionAttribute: Attribute

   3: {

   4:     public string HandleErrorAction { get; private set; }

   5:     public HandleErrorActionAttribute(string handleErrorAction = "")

   6:     {

   7:         this.HandleErrorAction = handleErrorAction;

   8:     }

   9: }

  通过HandleErrorActionAttribute特性设置的Handle-Error-Action需要手工执行以实现对当前请求的响 应,为此我们创建了一个具有如下定义的HandleErrorActionInvoker。它是ControllerActionInvoker的子 类,Handle-Error-Action需要手工执行以实现对当前请求的响应的执行通过虚方法InvokeActionMethod来完成。 ExtendedController的HandleErrorActionInvoker返回的就是这样一个对象。

   1: public class HandleErrorActionInvoker: ControllerActionInvoker

   2: {

   3:     public virtual ActionResult InvokeActionMethod(ControllerContext controllerContext, ActionDescriptor actionDescriptor)

   4:     {

   5:         IDictionary<string, object> parameterValues = this.GetParameterValues(controllerContext, actionDescriptor);

   6:         return base.InvokeActionMethod(controllerContext, actionDescriptor, parameterValues);

   7:     }

   8: }

二、实现在OnException方法中的异常处理逻辑

  整个异常处理和最终对请求的相应实现在如下所示的OnException方法中,流程并不复杂,在这里就不一一赘述了。不过对于整个处理流程,有两 个点值得一提:其一,在调用EntLib的EHAB对异常处理过程中,允许相应的ExceptionHandler设置一个友好的错误消息,而这个消息被 保存在当前HttpContext的Items中。其二,在调用异常处理方法之前,我们错误消息添加到当前的ModelState中,这也是为什么在上面 的实例演示中错误消息会自动出现在ValidationSummary中的根本原因。

   1: public class ExtendedController: Controller

   2: {    

   3:     //其他成员

   4:     protected override void OnException(ExceptionContext filterContext)

   5:     {

   6:         //或者当前的ExceptionPolicy,如果不存在,则直接调用基类OnException方法

   7:         string exceptionPolicyName = this.GetExceptionPolicyName();

   8:         if (string.IsNullOrEmpty(exceptionPolicyName))

   9:         {

  10:             base.OnException(filterContext);

  11:             return;

  12:         }

  13:  

  14:         //利用EntLib的EHAB进行异常处理,并获取错误消息和最后抛出的异常

  15:         filterContext.ExceptionHandled = true;

  16:         Exception exceptionToThrow;

  17:         string errorMessage;

  18:         try

  19:         {

  20:             ExceptionPolicy.HandleException(filterContext.Exception,exceptionPolicyName, out exceptionToThrow);

  21:             errorMessage = System.Web.HttpContext.Current.GetErrorMessage();

  22:         }

  23:         finally

  24:         {

  25:             System.Web.HttpContext.Current.ClearErrorMessage();

  26:         }

  27:         exceptionToThrow = exceptionToThrow ?? filterContext.Exception;

  28:  

  29:         //对于Ajax请求,直接返回一个用于封装异常的JsonResult

  30:         if (Request.IsAjaxRequest())

  31:         {

  32:             filterContext.Result = Json(new ExceptionDetail(exceptionToThrow, errorMessage));

  33:             return;

  34:         }

  35:  

  36:         //如果设置了匹配的HandleErrorAction,则调用之;

  37:         //否则将Error View呈现出来

  38:         string handleErrorAction = this.GetHandleErrorActionName();

  39:         string controllerName = ControllerContext.RouteData.GetRequiredString("controller");

  40:         string actionName = ControllerContext.RouteData.GetRequiredString("action");

  41:         errorMessage = string.IsNullOrEmpty(errorMessage) ? exceptionToThrow.Message : errorMessage;

  42:         if (string.IsNullOrEmpty(handleErrorAction))

  43:         {

  44:             filterContext.Result = View("Error", new ExtendedHandleErrorInfo(exceptionToThrow, controllerName, actionName, errorMessage));

  45:         }

  46:         else

  47:         {

  48:             ActionDescriptor actionDescriptor = Descriptor.FindAction(ControllerContext, handleErrorAction);                

  49:             ModelState.AddModelError("", errorMessage);

  50:             filterContext.Result = this.HandleErrorActionInvoker.InvokeActionMethod(ControllerContext, actionDescriptor);

  51:         }

  52:     }      

  53: }

三、将处理后的错误消息存放在HttpContext的Items中

  在调用EntLib的EHAB进行异常处理之后从当前HttpContext提取错误消息,以及最后清除消息分别是通过HttpContext的扩 展方法GetErrorMessage和ClearErrorMessage实现的。如下面的代码片断所示,除了这两个扩展方法我们还定义了另一个用于设 置错误消息的SetErrorMessage方法。

   1: public static class HttpContextExtensions

   2: {

   3:     public static string keyOfErrorMessage = Guid.NewGuid().ToString();

   4:  

   5:     public static void SetErrorMessage(this HttpContext context, string errorMessage)

   6:     {

   7:         context.Items[keyOfErrorMessage]=errorMessage;

   8:     }

   9:  

  10:     public static string GetErrorMessage(this HttpContext context)

  11:     {

  12:         return context.Items[keyOfErrorMessage] as string;

  13:     }

  14:  

  15:     public static void ClearErrorMessage(this HttpContext context)

  16:     {

  17:         if (context.Items.Contains(keyOfErrorMessage))

  18:         {

  19:             context.Items.Remove(keyOfErrorMessage);

  20:         }            

  21:     }

  22: }

四、用于设置错误消息的ErrorMessageHandler

  用于设置错误信息的ErrorMessageHandler以及对应配置元素类型ErrorMessageHandlerData定义如下。 ErrorMessageHandler表示错误消息的ErrorMessage属性在构造函数中被初始化,而在实现的HandleException方 法中直接通过调用当前HttpContext的扩展方法SetErrorMessage进行错误消息的设置。

   1: [ConfigurationElementType(typeof(ErrorMessageHandlerData))]

   2: public class ErrorMessageHandler: IExceptionHandler

   3: {

   4:     public string ErrorMessage { get; private set; }

   5:     public ErrorMessageHandler(string errorMessage)

   6:     {

   7:         this.ErrorMessage = errorMessage;

   8:     }

   9:     public Exception HandleException(Exception exception, Guid handlingInstanceId)

  10:     {

  11:         if (null != HttpContext.Current)

  12:         {

  13:             HttpContext.Current.SetErrorMessage(this.ErrorMessage);

  14:         }

  15:         return exception;

  16:     }

  17: }

  18:  

  19: public class ErrorMessageHandlerData : ExceptionHandlerData

  20: {

  21:     [ConfigurationProperty("errorMessage", IsRequired=true)]

  22:     public string ErrorMessage

  23:     {

  24:         get { return (string)this["errorMessage"]; }

  25:         set { this["errorMessage"] = value; }

  26:     }

  27:  

  28:     public override IEnumerable<TypeRegistration> GetRegistrations(string namePrefix)

  29:     {

  30:         yield return new TypeRegistration<IExceptionHandler>(() => new ErrorMessageHandler(this.ErrorMessage))

  31:         {

  32:             Name = this.BuildName(namePrefix),

  33:             Lifetime = TypeRegistrationLifetime.Transient

  34:         };

  35:     }

  36: }

作者:Artech
出处:http://artech.cnblogs.com/

加支付宝好友偷能量挖...


评论(1)网络
阅读(96)喜欢(0)asp.net-mvc