配色: 字号:
ASP.NET MVC集成EntLib实现“自动化”异常处理
2016-10-07 | 阅:  转:  |  分享 
  
ASP.NETMVC集成EntLib实现“自动化”异常处理[实现篇]

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



对于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:publicclassExtendedController:Controller

2:{

3:privatestaticDictionarycontrollerDescriptors=newDictionary();

4:privatestaticobjectsyncHelper=newobject();

5:

6:protectedoverridevoidOnException(ExceptionContextfilterContext)

7:{

8://省略成员

9:}

10:

11://描述当前Controller的ControllerDescriptor

12:publicControllerDescriptorDescriptor

13:{

14:get

15:{

16:ControllerDescriptordescriptor;

17:if(controllerDescriptors.TryGetValue(this.GetType(),outdescriptor))

18:{

19:returndescriptor;

20:}

21:lock(syncHelper)

22:{

23:if(controllerDescriptors.TryGetValue(this.GetType(),outdescriptor))

24:{

25:returndescriptor;

26:}

27:else

28:{

29:descriptor=newReflectedControllerDescriptor(this.GetType());

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

31:returndescriptor;

32:}

33:}

34:}

35:}

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

37:publicstringGetExceptionPolicyName()

38:{

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

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

41:if(null==actionDescriptor)

42:{

43:returnstring.Empty;

44:}

45:ExceptionPolicyAttributeexceptionPolicyAttribute=actionDescriptor.GetCustomAttributes(true).OfType().FirstOrDefault()??

46:Descriptor.GetCustomAttributes(true).OfType().FirstOrDefault()??newExceptionPolicyAttribute("");

47:returnexceptionPolicyAttribute.ExceptionPolicyName;

48:}

49:

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

51:publicstringGetHandleErrorActionName()

52:{

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

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

55:if(null==actionDescriptor)

56:{

57:returnstring.Empty;

58:}

59:HandleErrorActionAttributehandleErrorActionAttribute=actionDescriptor.GetCustomAttributes(true).OfType().FirstOrDefault()??

60:Descriptor.GetCustomAttributes(true).OfType().FirstOrDefault()??newHandleErrorActionAttribute("");

61:returnhandleErrorActionAttribute.HandleErrorAction;

62:}

63:

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

65:publicHandleErrorActionInvokerHandleErrorActionInvoker{get;privateset;}

66:

67:publicExtendedController()

68:{

69:this.HandleErrorActionInvoker=newHandleErrorActionInvoker();

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:publicclassExceptionPolicyAttribute:Attribute

3:{

4:publicstringExceptionPolicyName{get;privateset;}

5:publicExceptionPolicyAttribute(stringexceptionPolicyName)

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:publicclassHandleErrorActionAttribute:Attribute

3:{

4:publicstringHandleErrorAction{get;privateset;}

5:publicHandleErrorActionAttribute(stringhandleErrorAction="")

6:{

7:this.HandleErrorAction=handleErrorAction;

8:}

9:}

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



1:publicclassHandleErrorActionInvoker:ControllerActionInvoker

2:{

3:publicvirtualActionResultInvokeActionMethod(ControllerContextcontrollerContext,ActionDescriptoractionDescriptor)

4:{

5:IDictionaryparameterValues=this.GetParameterValues(controllerContext,actionDescriptor);

6:returnbase.InvokeActionMethod(controllerContext,actionDescriptor,parameterValues);

7:}

8:}



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

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



1:publicclassExtendedController:Controller

2:{

3://其他成员

4:protectedoverridevoidOnException(ExceptionContextfilterContext)

5:{

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

7:stringexceptionPolicyName=this.GetExceptionPolicyName();

8:if(string.IsNullOrEmpty(exceptionPolicyName))

9:{

10:base.OnException(filterContext);

11:return;

12:}

13:

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

15:filterContext.ExceptionHandled=true;

16:ExceptionexceptionToThrow;

17:stringerrorMessage;

18:try

19:{

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

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(newExceptionDetail(exceptionToThrow,errorMessage));

33:return;

34:}

35:

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

37://否则将ErrorView呈现出来

38:stringhandleErrorAction=this.GetHandleErrorActionName();

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

40:stringwww.baiyuewang.netactionName=ControllerContext.RouteData.GetRequiredString("action");

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

42:if(string.IsNullOrEmpty(handleErrorAction))

43:{

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

45:}

46:else

47:{

48:ActionDescriptoractionDescriptor=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:publicstaticclassHttpContextExtensions

2:{

3:publicstaticstringkeyOfErrorMessage=Guid.NewGuid().ToString();

4:

5:publicstaticvoidSetErrorMessage(thisHttpContextcontext,stringerrorMessage)

6:{

7:context.Items[keyOfErrorMessage]=errorMessage;

8:}

9:

10:publicstaticstringGetErrorMessage(thisHttpContextcontext)

11:{

12:returncontext.Items[keyOfErrorMessage]asstring;

13:}

14:

15:publicstaticvoidClearErrorMessage(thisHttpContextcontext)

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:publicclassErrorMessageHandler:IExceptionHandler

3:{

4:publicstringErrorMessage{get;privateset;}

5:publicErrorMessageHandler(stringerrorMessage)

6:{

7:this.ErrorMessage=errorMessage;

8:}

9:publicExceptionHandleException(Exceptionexception,GuidhandlingInstanceId)

10:{

11:if(null!=HttpContext.Current)

12:{

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

14:}

15:returnexception;

16:}

17:}

18:

19:publicclassErrorMessageHandlerData:ExceptionHandlerData

20:{

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

22:publicstringErrorMessage

23:{

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

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

26:}

27:

28:publicoverrideIEnumerableGetRegistrations(stringnamePrefix)

29:{

30:yieldreturnnewTypeRegistration(()=>newErrorMessageHandler(this.ErrorMessage))

31:{

32:Name=this.BuildName(namePrefix),

33:Lifetime=TypeRegistrationLifetime.Transient

34:};

35:}

36:}

献花(0)
+1
(本文系thedust79首藏)