基于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() == ) && 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 = ;
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 != )
{ //写入日志 记录
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() > )
{
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框架确实比以前改进了很多,为我们对于代码的精化及设计思路上更进了一步,诸如异常捕获,权限验证等也能很好的体现出来。如果你还是以前的程序猿,现在也算体会到了设计师的畅快感,不是吗?
出处:http://www.cnblogs.com/laogu2/ 欢迎转载,但任何转载必须保留完整文章,在显要地方显示署名以及原文链接。如您有任何疑问或者授权方面的协商,请给我留言。
基于AOP的MVC拦截异常让代码更优美的更多相关文章
- 基于AOP的优惠券发送异常哨兵监控
本文来自网易云社区 作者:王贝 最近总是发现支付发红包优惠券发完的情况,但是发现的比较迟缓,于是乎,想加一个哨兵监控,统计了一下,组内不少需求都有发送优惠券的行为,也是经常遇到发送异常的情况,所以,想 ...
- Spring MVC拦截器完整代码示例
拦截器的作用: 编写一个自定义的类,实现相关拦截器接口: preHandler不放行,直接return false:直接跳转到错误页面error.jsp postHandler后置处理器,也就是C ...
- 基础才是重中之重~ConcurrentDictionary让你的多线程代码更优美
回到目录 ConcurrentDictionary是.net4.0推出的一套线程安全集合里的其中一个,和它一起被发行的还有ConcurrentStack,ConcurrentQueue等类型,它们的单 ...
- sublime text 的小细节设置,让你的代码更优美
这些属性都可以在 首选项>设置-默认 里修改下面也会介绍几个比较常用的几个插件 字体大小: "font_size": 17 高亮编辑中的一行 "highlight_ ...
- ES6几大特性,让你的代码更优美
1.Default Parameters(默认参数) in ES6 还记得我们以前不得不通过下面方式来定义默认参数: var link = function (height, color, url ...
- ConcurrentDictionary让你的多线程代码更优美
ConcurrentDictionary是.net4.0推出的一套线程安全集合里的其中一个,和它一起被发行的还有ConcurrentStack,ConcurrentQueue等类型,它们的单线程版本( ...
- ASP.NET MVC案例教程(基于ASP.NET MVC beta)——第六篇:拦截器
摘要 本文将对“MVC公告发布系统”的发布公告功能添加日志功能和异常处理功能,借此来讨论ASP.NET MVC中拦截器的使用方法. 一个小难题 我们继续完善“MVC公告发布系统”, ...
- Asp.net Mvc 身份验证、异常处理、权限验证(拦截器)实现代码
本问主要介绍asp.net的身份验证机制及asp.net MVC拦截器在项目中的运用.现在让我们来模拟一个简单的流程:用户登录>权限验证>异常处理 1.用户登录 验证用户是否登录成功步骤直 ...
- 全新升级的AOP框架Dora.Interception[4]: 基于Lambda表达式的拦截器注册方式
如果拦截器应用的目标类型是由自己定义的,Dora.Interception(github地址,觉得不错不妨给一颗星)可以在其类型或成员上标注InterceptorAttribute特性来应用对应的拦截 ...
随机推荐
- CMS模板应用调研问卷
截止目前,已经有数十家网站与我们合作,进行了MIP化改造,在搜索结果页也能看到"闪电标"的出现.除了改造方面的问题,MIP项目组被问到最多的就是:我用了wordpress,我用了织 ...
- 以bank account 数据为例,认识elasticsearch query 和 filter
Elasticsearch 查询语言(Query DSL)认识(一) 一.基本认识 查询子句的行为取决于 query context filter context 也就是执行的是查询(query)还是 ...
- js复杂对象和简单对象的简单转化
var course = { teacher :{ teacherId:001, teacherName:"王" }, course : { courseId : 120, cou ...
- C# MVC 5 - 生命周期(应用程序生命周期&请求生命周期)
本文是根据网上的文章总结的. 1.介绍 本文讨论ASP.Net MVC框架MVC的请求生命周期. MVC有两个生命周期,一为应用程序生命周期,二为请求生命周期. 2.应用程序生命周期 应用程序生命周期 ...
- C# 条形码操作【源码下载】
本篇介绍通过C#生成和读取一维码.二维码的操作. 目录 1. 介绍:介绍条形码.条形码的分类以及ZXing.Net类库. 2. 一维码操作:包含对一维码的生成.读取操作. 3. 二维码操作:包含对二维 ...
- JavaScript String对象
本编主要介绍String 字符串对象. 目录 1. 介绍:阐述 String 对象的说明以及定义方式. 2. 实例属性:介绍 String 对象的实例属性: length. 3. 实例方法:介绍 St ...
- 【原创】免费申请SSL证书【用于HTTPS,即是把网站从HTTP改为HTTPS,加密传输数据,保护敏感数据】
今天公司有个网站需要改用https访问,所以就用到SSL证书.由于沃通(以前我是在这里申请的)暂停了免费的SSL证书之后,其网站推荐了新的一个网站来申请证书,所以,今天因为刚好又要申请一个证书,所以, ...
- android studio你可能忽视的细节——启动白屏?drawable和mipmap出现的意义?这里都有!!!
android studio用了很久了,也不知道各位小伙伴有没有还在用eclipse的,如果还有,楼主真心推荐转到android studio来吧,毕竟亲儿子,你会知道除了启动速度稍微慢些,你找不到一 ...
- MongoDB系列(二):C#应用
前言 上一篇文章<MongoDB系列(一):简介及安装>已经介绍了MongoDB以及其在window环境下的安装,这篇文章主要讲讲如何用C#来与MongoDB进行通讯.再次强调一下,我使用 ...
- Tomcat常见问题及常用命令
很长时间不用tomcat好多命令都忘记了,所以准备自己记录下来,以便参考.刚好也希望可以开始养成记博客的好习惯. 1.查看java的版本号 进入java的安装目录后,使用命令:java -versio ...