Java自学者论坛

 找回密码
 立即注册

手机号码,快捷登录

恭喜Java自学者论坛(https://www.javazxz.com)已经为数万Java学习者服务超过8年了!积累会员资料超过10000G+
成为本站VIP会员,下载本站10000G+会员资源,会员资料板块,购买链接:点击进入购买VIP会员

JAVA高级面试进阶训练营视频教程

Java架构师系统进阶VIP课程

分布式高可用全栈开发微服务教程Go语言视频零基础入门到精通Java架构师3期(课件+源码)
Java开发全终端实战租房项目视频教程SpringBoot2.X入门到高级使用教程大数据培训第六期全套视频教程深度学习(CNN RNN GAN)算法原理Java亿级流量电商系统视频教程
互联网架构师视频教程年薪50万Spark2.0从入门到精通年薪50万!人工智能学习路线教程年薪50万大数据入门到精通学习路线年薪50万机器学习入门到精通教程
仿小米商城类app和小程序视频教程深度学习数据分析基础到实战最新黑马javaEE2.1就业课程从 0到JVM实战高手教程MySQL入门到精通教程
查看: 565|回复: 0

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

[复制链接]
  • TA的每日心情
    奋斗
    2025-3-18 14:43
  • 签到天数: 805 天

    [LV.10]以坛为家III

    2053

    主题

    2111

    帖子

    73万

    积分

    管理员

    Rank: 9Rank: 9Rank: 9

    积分
    731050
    发表于 2021-5-27 09:07:19 | 显示全部楼层 |阅读模式

    通过《实例篇》的实演示可以看出我们通过扩展实现的自动异常处理机制能够利用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实现“自动化”异常处理[实现篇]

    哎...今天够累的,签到来了1...
    回复

    使用道具 举报

    您需要登录后才可以回帖 登录 | 立即注册

    本版积分规则

    QQ|手机版|小黑屋|Java自学者论坛 ( 声明:本站文章及资料整理自互联网,用于Java自学者交流学习使用,对资料版权不负任何法律责任,若有侵权请及时联系客服屏蔽删除 )

    GMT+8, 2025-4-21 07:06 , Processed in 0.071562 second(s), 29 queries .

    Powered by Discuz! X3.4

    Copyright © 2001-2021, Tencent Cloud.

    快速回复 返回顶部 返回列表