博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
ASP.NET MVC集成EntLib实现“自动化”异常处理[实现篇]
阅读量:7027 次
发布时间:2019-06-28

本文共 10983 字,大约阅读时间需要 36 分钟。

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

  • 对于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
controllerDescriptors = new Dictionary
();
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
().FirstOrDefault()??
46:            Descriptor.GetCustomAttributes(true).OfType
().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
().FirstOrDefault()??
60:             Descriptor.GetCustomAttributes(true).OfType
().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
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
GetRegistrations(string namePrefix)
29:     {
30:         yield return new TypeRegistration
(() => new ErrorMessageHandler(this.ErrorMessage))
31:         {
32:             Name = this.BuildName(namePrefix),
33:             Lifetime = TypeRegistrationLifetime.Transient
34:         };
35:     }
36: }

 

转载地址:http://mkrxl.baihongyu.com/

你可能感兴趣的文章
Lodash源码分析-compact.js
查看>>
度小满牵手南京银行打造”AI鑫”计划:银行零售业掀起变革运动
查看>>
微信小程序之分享海报生成
查看>>
敏捷AI|NLP技术在宜信业务中的实践「背景篇」
查看>>
布局结束检测工具
查看>>
[MetalKit]21-What's-new-in-graphics-and-games-at-WWDC-2016
查看>>
html2canvas在vue下的巨坑
查看>>
从设计师和开发的角度使用 lottie
查看>>
iOS之自定义tabBar
查看>>
Spring boot学习(三) Spring boot整合mybatis
查看>>
Redux 源码深度解析(附带视频1月14号上传)
查看>>
理解webpack原理,手写一个100行的webpack
查看>>
Node.js & Express 项目基本搭建
查看>>
掌握 MySQL 这 19 个骚操作,效率至少提高3倍
查看>>
【跃迁之路】【744天】程序员高效学习方法论探索系列(实验阶段501-2019.3.6)...
查看>>
用于大数据测试、学习的测试数据
查看>>
Software System Analysis and Design | 1
查看>>
JavaScript函数式编程,真香之组合(一)
查看>>
JavaScript链式调用实例浅析
查看>>
报表没完没了怎么办? | 润乾集算器提效报表开发
查看>>