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

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

 

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

 

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

经过翻译中文是这样的架构

其中有一个异常过滤器mvc

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

看看这个的源码是怎么能出的asp.net

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}ide

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

 

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

 

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

 

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

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

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

 

2、自定义咱们的异常类


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

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错误页里配置的话没有问题!

<customErrors mode="On">

//在此节里配置不会获得正确的404 StateCode
</customErrors>

 

提倡的应该在 HttpErrors节点配置

<httpErrors errorMode="Custom" existingResponse="Replace">
<clear/>
<error statusCode="404" path="ErrorPages\404.html"/>
<error statusCode="500" path="ErrorPages\500.html"/>
</httpErrors>

 

 再看看打印出的404页面状态码

 

此次就正确了,因此404页要交给IIS处理,咱们上述代码没有处理404页,若是处理的话,那么可能得不到StatusCode=404的状态码,表面上UI没有任何问题,但对于SEO而言,死链得不到及时处理。

话题转回来,继续咱们的文章。

 

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

如今咱们测试下,这两种异常吧,看看代码是否是’优美‘了许多

前台Test Code:

 

后台Test Code:

 

 

测试结果:

UI层捕获显示

 

Log txt Exception:

 

 

 再看看,其它的测试方式?

 正常test:

 

 

修改下前台code,后台代码不变,进行异常test:

全局捕获的异常日志:

 

 

对比之前的try...catch...话,明显前者漂亮了许多。

 

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

 

做者: 谷歌's谷歌's博客园
出处: http://www.cnblogs.com/laogu2/ 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文连接。如您有任何疑问或者受权方面的协商,请 给我留言
相关文章
相关标签/搜索