好得很程序员自学网

<tfoot draggable='sEl'></tfoot>

ASP.NET MVC集成EntLib实现“自动化”异常处理[实现篇]

ASP.NET MVC集成EntLib实现“自动化”异常处理[实现篇]

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

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

目录 
一、ExceptionPolicyAttribute & HandleErrorActionAttribute 
二、实现在OnException方法中的异常处理逻辑 
三、将处理后的错误消息存放在HttpContext的Items中 
四、用于设置错误消息的ErrorMessageHandler

一、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:  }

ASP.NET MVC集成EntLib实现“自动化”异常处理[实例篇]  
ASP.NET MVC集成EntLib实现“自动化”异常处理[实现篇]

作者: Artech
出处: http://artech.cnblogs测试数据/
本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

分类:  [02] 编程技巧 ,  [14] 框架设计

作者: Leo_wl

    

出处: http://HdhCmsTestcnblogs测试数据/Leo_wl/

    

本文版权归作者和博客园共有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

版权信息

查看更多关于ASP.NET MVC集成EntLib实现“自动化”异常处理[实现篇]的详细内容...

  阅读:53次