基于AOP的MVC拦截异常让代码更优美

与asp.net 打交道很多年,如今天微软的优秀框架越来越多,其中微软在基于mvc的思想架构,也推出了自己的一套asp.net mvc 框架,如果你亲身体验过它,会情不自禁的说‘漂亮’。回过头来,‘漂亮’终归有个好的思想,其中类似于AOP的思想,就在其中体现的淋漓尽致,今天本文主要讨论的是基于AOP思想构成的‘异常过滤器’。我们的目的只有一个,让try...catch...无处盾形,让代码更健壮优美。

一、理解mvc里filter是怎么运行的

老外的一篇文章是这样的草图

通过翻译中文是这样的

其中有一个异常过滤器

通过上述的表格可以清楚的看出,当Controller或Action执行时,IExceptionFiter的实现基类都将有‘能力’处理的,其中微软在mvc中默认实现了一个实现类HandleErrorAttribute

看看这个的源码是怎么能出的

public virtual void OnException(ExceptionContext filterContext)
        {
            if (filterContext == null)
            {
                throw new ArgumentNullException("filterContext");
            }
            if (!filterContext.IsChildAction && (!filterContext.ExceptionHandled && filterContext.HttpContext.IsCustomErrorEnabled))
            {
                Exception innerException = filterContext.Exception;
                if ((new HttpException(null, innerException).GetHttpCode() == 500) && this.ExceptionType.IsInstanceOfType(innerException))
                {
                    string controllerName = (string) filterContext.RouteData.Values["controller"];
                    string actionName = (string) filterContext.RouteData.Values["action"];
                    HandleErrorInfo model = new HandleErrorInfo(filterContext.Exception, controllerName, actionName);
                    ViewResult result = new ViewResult {
                        ViewName = this.View,
                        MasterName = this.Master,
                        ViewData = new ViewDataDictionary<HandleErrorInfo>(model),
                        TempData = filterContext.Controller.TempData
                    };
                    filterContext.Result = result;
                    filterContext.ExceptionHandled = true;
                    filterContext.HttpContext.Response.Clear();
                    filterContext.HttpContext.Response.StatusCode = 500;
                    filterContext.HttpContext.Response.TrySkipIisCustomErrors = true;
                }
            }
        }

这些代码的思路是这样的

HandleErrorAttribute-->HandleErrorInfo(model)-->ViewResult-->{ViewName:Error}

即 抛出一个Share文件夹里的Error视图。

可以看出以Filter形式给出的,说明它有能力以特性的形式‘贴’在控制器Controller或Action上,让代码更为‘优美’!因为Filter微软也给我们留了‘FilterConfig’入口,可以全局性的‘注入’

至此,我们可以看出,有至少有两种形式来规划的我的‘异常’,一是以特性的形式‘贴’在Controller和Action上,二是,以全局的‘FilterConfig’注册。不管怎么样,结果只有一个,try...catch...离我们渐渐远去了,不是吗?那我们继续看下去!

异常通过以‘里‘向‘外‘抛出去的,就像我们常常看到的异常黄页,那个是抛到最外面的一个异常页面。

1.Action-->HandleErrorAttribute(默认)-->IExceptionFilter-->OnException

2.Controller-->HandleErrorAttribute(默认)-->IExceptionFilter-->OnException

二、自定义我们的异常类


在自定义我们的异常辅助类前,我们首先明确的几点:

1.全局的异常用CustomExceptionAttribute自定义异常类来捕获。

2.对于Action是JsonResult的用Json格式的‘事件’用‘AsyncExceptionAttribute’异常来捕获。

顺序是:Action-->AsyncExceptionAttribute-->CustomExceptionAttribute-->HandleErrorAttribute(默认)-->IExceptionFilter-->OnException

在Action里发生异常,如果是JsonResult,用AsyncExceptionAttribute来捕获出去,然后进行全局的CustomExceptionAttribute

当设定 filterContext.ExceptionHandled=True时 表示此异常’已被处理‘,反之异常在后续的捕获机制中向外抛出,这个由自己的需要自由订制。

1)AsyncExceptionAttribute定义:

如果是JsonResult异常的话,我们进行处理组合成一个Data,状态为success=false,message=‘异常信息‘。

这里没有做filterContext.ExceptionHandled=True处理,为了让异常向’外抛‘CustomExceptionAttribute 处理,因为我们要记录这个异常日志,而不是仅仅的显示给UI界面。

2)CustomExceptionAttribute全局异常:

 public override void OnException(ExceptionContext filterContext)
        {

            int StatusCode = filterContext.HttpContext.Response.StatusCode;

            if (filterContext.Exception != null && StatusCode != 404)
            {

                //写入日志 记录
                string message = string.Format("------------------------------------------------------------------------------------------------------------------------------------------------------------\r\n下面是捕获的异常信息摘要:\r\n 时间:{0}\r\n消息类型:{1}\r\n 消息内容:{2}\r\n 引发异常的方法:{3}\r\n 引发异常源:{4}"
                     , DateTime.Now
                     , filterContext.Exception.GetType().Name
                     , filterContext.Exception.Message
                     , filterContext.Exception.TargetSite
                     , filterContext.Exception.Source + filterContext.Exception.StackTrace
                     );

                DbEntityValidationException e = filterContext.Exception as DbEntityValidationException;

                if (e != null && e.EntityValidationErrors.Count() > 0)
                {
                    System.Text.StringBuilder tempDate = new StringBuilder("\r\n下面是捕获的验证类详细信息:\r\n");

                    foreach (var eve in e.EntityValidationErrors)
                    {
                        tempDate.AppendLine(string.Format("Entity of type \"{0}\" in state \"{1}\" has the following validation errors:",
           eve.Entry.GetType().Name, eve.Entry.State));

                        foreach (var ve in eve.ValidationErrors)
                        {
                            tempDate.AppendLine(string.Format("- Property: \"{0}\", Error: \"{1}\"",
                                ve.PropertyName, ve.ErrorMessage));
                        }
                    }

                    message += tempDate.Append("\r\n").ToString();
                }

                Task t = new Task(() =>
                {
                    WL.Common.SysLogHelp.WriteLogFile("ErrorLog", message, filterContext.HttpContext);
                });

                t.Start();
                //t.Wait();
            }

            if (filterContext.Result is JsonResult)
                filterContext.ExceptionHandled = true;
            else
                base.OnException(filterContext);

        }

CustomExceptionAttribute全局异常定交方法,首先排除不是404的异常,因为,404可能是死链,网站不应该处理,交给IIS级的异常程程序处理。

IIS配置管理员(windows 2012)

IIS 配置管理器里有两个’错误‘处理机制,在.NET配选项里配置404页,有个一潜在的问题StatusCode=304而不是404,微软也有相关的说明,而在IIS错误页里配置的话没有问题!

我们上述代码没有处理404页,如果处理的话,那么可能得不到StatusCode=404的状态码,表面上UI没有任何问题,但对于SEO而言,死链得不到及时处理。

同时上面的代码对于filterContext.Result is JsonResult 设置filterContext.ExceptionHandled = true,而对于其它的异常base.OnException(filterContext),继续向外面处理,防止有其它异常不能及时处理。我们用了Task任务异步来写入日志,写日志毕竟也是浪费时间的嘛。

现在我们测试下,这两种异常吧,看看代码是不是’优美‘了许多

测试异常日志text文件

两段代码的对比,明显前者漂亮了许多。有没有心动,快点引用到自己的mvc框架吧!

小结:在传统的web form 框架里对于集成AOP思想,是一个较操心的事情,微软也看到了这点,新的mvc框架确实比以前改进了很多,为我们对于代码的精化及设计思路上更进了一步,诸如异常捕获,权限验证等也能很好的体现出来。如果你还是以前的程序猿,现在也算体会到了设计师的畅快感,不是吗?

 

时间: 2024-11-06 17:24:57

基于AOP的MVC拦截异常让代码更优美的相关文章

ConcurrentDictionary让你的多线程代码更优美

ConcurrentDictionary是.net4.0推出的一套线程安全集合里的其中一个,和它一起被发行的还有ConcurrentStack,ConcurrentQueue等类型,它们的单线程版本(线程不安全的,Queue,Stack,Dictionary)我们一定不会陌生,可以说是经常用到,一个类的实例里,有个属性是个字典,我们不加考虑的会用Dictionary,而当这个属性被提升为static静态的(类级别的)时候,我们就要考虑它的线程安全性了,因为它有可能被多个线程同时访问,当然,如果这

asp.net mvc,基于aop实现的接口访问统计、接口缓存等

其实asp.net 上aop现有的框架应该蛮多的,比如静态注入式的PostSharp(新版本好像已经商业化了,旧版本又不支持.net4.0+),或者通过反射的(性能会降低). 本文则是通过mvc其中一种方法拦截器ActionFilter(参考网上已经有很多类似例子). 首先新建一个日志控制类,命名为ApiLogAttribute,继承于ActionFilterAttribute /// <summary> /// 记录访问日志以及站点安全检查 /// </summary> publ

Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码

本问主要介绍asp.net的身份验证机制及asp.net MVC拦截器在项目中的运用.现在让我们来模拟一个简单的流程:用户登录>权限验证>异常处理 1.用户登录 验证用户是否登录成功步骤直接忽略,用户登录成功后怎么保存当前用户登录信息(session,cookie),本文介绍的是身份验证(其实就是基于cookie)的,下面看看代码. 引入命名空间 using System.Web.Security; Users ModelUser = new Users() { ID = 10000, Nam

从壹开始前后端分离 40 || 完美基于AOP的接口性能分析

旁白音:本文是不定时更新的.net core,当前主线任务的Nuxt+VueAdmin教程的 nuxt.js 之 tibug项目已上线,大家可以玩一玩:http://123.206.33.109:7090,具体的部署教程会在下周发表. 缘起 哈喽大家周五好呀,今天是一个不定时更新的文章,是很简单的一篇文章,大家应该都能看懂,主要包含了两个内容,一个是对AOP编程的进一步的理解(其中还有和过滤器比较),第二个就是一个简单的小插件——记录接口的调用时间调用情况,也就是很简单的性能记录,这个时候你肯定

Spring AOP原理及拦截器

原理 AOP(Aspect Oriented Programming),也就是面向方面编程的技术.AOP基于IoC基础,是对OOP的有益补充. AOP将应用系统分为两部分,核心业务逻辑(Core business concerns)及横向的通用逻辑,也就是所谓的方面Crosscutting enterprise concerns,例如,所有大中型应用都要涉及到的持久化管理(Persistent).事务管理(Transaction Management).安全管理(Security).日志管理(L

项目一:第十二天 1、常见权限控制方式 2、基于shiro提供url拦截方式验证权限 3、在realm中授权 5、总结验证权限方式(四种) 6、用户注销7、基于treegrid实现菜单展示

1 课程计划 1. 常见权限控制方式 2. 基于shiro提供url拦截方式验证权限 3. 在realm中授权 4. 基于shiro提供注解方式验证权限 5. 总结验证权限方式(四种) 6. 用户注销 7. 基于treegrid实现菜单展示 2 常见的权限控制方式 2.1 url拦截实现权限控制 shiro基于过滤器实现的   2.2 注解方式实现权限控制 底层:代理技术     3 基于shiro的url拦截方式验权   <!-- 配置过滤器工厂 --> <bean id="

Spring Boot 2.X(九):Spring MVC - 拦截器(Interceptor)

拦截器 1.简介 Spring MVC 中的拦截器(Interceptor)类似于 Servlet 开发中的过滤器 Filter,它主要用于拦截用户请求并作相应的处理,它也是 AOP 编程思想的体现,底层通过动态代理模式完成. 2.定义实现类 拦截器有两种实现方式: 1.实现 HandlerInterceptor 接口 2.继承 HandlerInterceptorAdapter 抽象类(看源码最底层也是通过 HandlerInterceptor 接口 实现) 3.HandlerIntercep

spring中基于aop使用ehcache

继续上篇,这篇介绍服务层缓存,基于aop的方式使用ehcache 一.修改配置文件 修改spring-context-ehcache.xml文件,加入: <!-- ehcache缓存实例 --> <bean id="testCacheInterceptor" class="org.springframework.cache.ehcache.EhCacheFactoryBean"> <property name="cacheMa

Java实战之03Spring-05Spring中的事务控制(基于AOP)

五.Spring中的事务控制(基于AOP) 1.Spring中事务有关的接口 1.1.明确: JavaEE体系进行分层开发,事务处理位于业务层,Spring提供了分层设计业务层的事务处理解决方案 1.2.Spring事务管理主要包括3个接口 1.2.1.PlatformTransactionManager事务管理器 具体实现类: 1.2.2.TransactionDefinition事务定义信息 事务的隔离级别: 事务的传播行为:有时面试会问到 REQUIRED:如果当前没有事务,就新建一个事务