一. 简介html
MVC中的过滤器能够说是MVC框架中的一种灵魂所在,它是MVC框架中AOP思想的具体体现,因此它以面向切面的形式无侵入式的做用于代码的业务逻辑,与业务逻辑代码分离,一经推出,广受开发者的喜好。web
那么过滤器究竟是什么呢?它又有什么做用呢?sql
用户经过URL访问Web系统不必定都能获得相应的内容,一方面不一样的用户权限不一样,另外一方面是为了保护系统,防止被攻击,这就是过滤器的核心所在,咱们总计一下过滤器都有哪些做用:数据库
①:判断用户是否登陆以及不一样用户对应不一样的权限问题。缓存
②:防盗链、防爬虫。session
③:系统中语言版本的切换(本地化和国际化)。框架
④:权限管理系统中动态Action。nosql
⑤:决策输出缓存。ide
知道到了过滤器的做用,那么过滤器分哪几类呢?以下图1:测试
二. 执行顺序
从上图①可知,过滤器分四类,总共重写了六个方法,在这六个方法里能够处理相应的业务逻辑,那么若是四种过滤器的六个重写方法同时存在,它们的执行顺序是什么呢?
首先要将OnException方法除外,该方法不和其他五个方法参与排序问题,该方法独立存在,什么时间报错,何时调用。
其他三种过滤器中的五个重写方法的执行顺序:
三. 自定义实现形式
1. 直接在控制器中重写方法或者利用控制器间的继承
新建任何一个控制器,它均继承Controller类,F12进入Controller类中,发现Controller类中已经实现了过滤器须要实现的接口,而且提供虚方法供咱们重写,代码以下:
基于以上原理,这样在控制器级别上咱们就有两种思路来实现过滤器。
方案一:直接在当前控制器重写相应的过滤器方法,则该过滤器的方法做用于当前控制器的全部Action。
方案二:新建一个父类控制器,在父类控制器中重写过滤器的方法,而后子类控制器继承该父类控制器,则该该过滤器做用于子类控制器中的全部Action。
【该方法和接下来以特性的形式做用于控制器的效果是一致的】
1 /// <summary> 2 /// 控制器继承该控制器,和特性做用在控制器上效果一致 3 /// </summary> 4 public class MyBaseFilterController : Controller 5 { 6 //须要用protected类型,不能用public类型 7 protected override void OnAuthorization(AuthorizationContext filterContext) 8 { 9 //1.若是保留以下代码,则会运行.net framework定义好的身份验证,若是但愿自定义身份验证,则删除以下代码 10 // base.OnAuthorization(filterContext); 11 12 //2.获取区域名字 13 // string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); 14 15 //3.获取控制器做用的Controller和action的名字 16 string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower(); 17 string actionName = filterContext.ActionDescriptor.ActionName.ToLower(); 18 filterContext.HttpContext.Response.Write("身份验证过滤器做用于" + controllerName + "控制器下的" + actionName + "方法</br>"); 19 } 20 }
2. 自定义类继承MVC中过滤器实现类或过滤器接口,特性的形式做用于控制器或Action
特别补充:MVC框架中的AuthorizeAttirbute、ActionFilterAttribute、HandleErrorAttribute类已经实现了过滤器对应的接口,因此咱们在自定义过滤器的时候,能够直接继承以上三个类;或者实现相应的接口:IAuthorizationFilter、IActionFilter、IResultFilter、IExceptionFilter。(该方案在实现相应接口的同时,须要继承FilterAttribute,使自定义的类成为一个特性)。
下面以继承MVC中实现类的形式来自定义四种过滤器:
A:身份验证过滤器
1 /// <summary> 2 /// 身份验证过滤器 3 /// 1. 在非MVC框架项目中使用MVC过滤器,须要经过nuget把MVC的程序集添加进去 4 /// 2. 继承AuthorizeAttribute类,而后对OnAuthorization方法进行 override 覆写 5 /// 3. 在Action运行以前首先运行该过滤器 6 /// </summary> 7 public class MyAuthorize : AuthorizeAttribute 8 { 9 public override void OnAuthorization(AuthorizationContext filterContext) 10 { 11 //1.若是保留以下代码,则会运行.net framework定义好的身份验证,若是但愿自定义身份验证,则删除以下代码 12 // base.OnAuthorization(filterContext); 13 14 //2.获取区域名字 15 // string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); 16 17 //3.获取控制器做用的Controller和action的名字 18 string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower(); 19 string actionName = filterContext.ActionDescriptor.ActionName.ToLower(); 20 filterContext.HttpContext.Response.Write("身份验证过滤器做用于" + controllerName + "控制器下的" + actionName + "方法</br>"); 21 } 22 }
B: 行为过滤器
1 /// <summary> 2 /// 行为过滤器 3 /// 1. 在非MVC框架项目中使用MVC过滤器,须要经过nuget把MVC的程序集添加进去 4 /// 2. 继承ActionFilterAttribute类,而后对OnActionExecuting方法和OnActionExecuted方法进行 override 覆写 5 /// 3. OnActionExecuting方法:在action方法运行以前,且OnAuthorization过滤器运行以后调用 6 /// OnActionExecuted方法:在action方法运行以后调用 7 /// </summary> 8 public class MyAction: ActionFilterAttribute 9 { 10 11 /// <summary> 12 /// 在action方法运行以前调用 13 /// </summary> 14 /// <param name="filterContext"></param> 15 public override void OnActionExecuting(ActionExecutingContext filterContext) 16 { 17 //1.若是保留以下代码,则会运行.net framework定义好的行为验证,若是但愿自定义行为验证,则删除以下代码 18 // base.OnActionExecuting(filterContext); 19 20 //2.获取区域名字 21 // string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); 22 23 //3.获取控制器做用的Controller和action的名字 24 string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower(); 25 string actionName = filterContext.ActionDescriptor.ActionName.ToLower(); 26 filterContext.HttpContext.Response.Write("行为过滤器OnActionExecuting做用于" + controllerName + "控制器下的" + actionName + "方法运行以前</br>"); 27 } 28 /// <summary> 29 /// 在action方法运行以后调用 30 /// </summary> 31 /// <param name="filterContext"></param> 32 public override void OnActionExecuted(ActionExecutedContext filterContext) 33 { 34 //1.若是保留以下代码,则会运行.net framework定义好的行为验证,若是但愿自定义行为验证,则删除以下代码 35 // base.OnActionExecuted(filterContext); 36 37 //2.获取区域名字 38 // string strAreaName = filterContext.RouteData.DataTokens["area"].ToString().ToLower(); 39 40 //3.获取控制器做用的Controller和action的名字 41 string controllerName = filterContext.ActionDescriptor.ControllerDescriptor.ControllerName.ToLower(); 42 string actionName = filterContext.ActionDescriptor.ActionName.ToLower(); 43 filterContext.HttpContext.Response.Write("行为过滤器OnActionExecuted做用于" + controllerName + "控制器下的" + actionName + "方法运行以后</br>"); 44 } 45 }
C:结果过滤器
1 /// <summary> 2 /// 结果过滤器 3 /// 1. 在非MVC框架项目中使用MVC过滤器,须要经过nuget把MVC的程序集添加进去 4 /// 2. 继承ActionFilterAttribute类,而后对OnResultExecuting方法和OnResultExecuted方法进行 override 覆写 5 /// 3. OnResultExecuting方法:在执行结果以后(action以后),页面渲染以前调用 6 /// OnResultExecuted方法:在页面渲染以后调用 7 /// </summary> 8 public class MyResult : ActionFilterAttribute 9 { 10 11 /// <summary> 12 /// action执行以后(OnActionExecuting以后),页面渲染以前调用 13 /// </summary> 14 /// <param name="filterContext"></param> 15 public override void OnResultExecuting(ResultExecutingContext filterContext) 16 { 17 //1.若是保留以下代码,则会运行.net framework定义好的结果验证,若是但愿自定义结果验证,则删除以下代码 18 // base.OnResultExecuting(filterContext); 19 20 //该方法中没法获取是哪一个控制器后 21 filterContext.HttpContext.Response.Write("结果过滤器OnResultExecuting做用于action运行以后,页面加载以前"); 22 } 23 /// <summary> 24 /// 页面渲染以后调用 25 /// </summary> 26 /// <param name="filterContext"></param> 27 public override void OnResultExecuted(ResultExecutedContext filterContext) 28 { 29 //1.若是保留以下代码,则会运行.net framework定义好的结果验证,若是但愿自定义结果验证,则删除以下代码 30 // base.OnResultExecuted(filterContext); 31 32 //该方法中没法获取是哪一个控制器后 33 filterContext.HttpContext.Response.Write("结果过滤器OnResultExecuted做用于页面渲染以后"); 34 } 35 }
D:异常过滤器
使用自定义异常处理,须要在web.config中为system.web添加<customErrors mode="On" />节点
1 /// <summary> 2 /// 异常过滤器 3 /// 须要注意的点: 4 /// ①:若是自定义异常过滤器且须要有做用于全局,须要把FilterConfig中的 filters.Add(new HandleErrorAttribute());注释掉, 5 /// 而后把自定义的异常过滤器添加到FilterConfig中。 6 /// ②:使用自定义异常处理,须要在web.config中为system.web添加<customErrors mode="On" />节点 7 /// </summary> 8 public class MyException: HandleErrorAttribute 9 { 10 public override void OnException(ExceptionContext filterContext) 11 { 12 //调用框架自己异常处理器的方法 13 base.OnException(filterContext); 14 15 //获取异常信息(能够根据实际须要写到本地或数据库中) 16 var errorMsg = filterContext.Exception; 17 18 //跳转指定的错误页面 19 filterContext.Result = new RedirectResult("/error.html"); 20 } 21 }
下面展现以特性的形式做用于控制器或控制器中的Action:
3. 自定义类继承MVC中实现类或接口,全局注册,做用于所有控制器
若是以上两种方式均不能知足你的过滤器的使用范围,你能够在App_Start文件夹下的FilterConfig类中进行全局注册,使该过滤器做用于全部控制器中全部Action方法。
特别注意的一点是:自定义异常过滤器,须要把系统默认的filters.Add(new HandleErrorAttribute());注释掉。
全局注册的代码以下:
1 public class FilterConfig 2 { 3 public static void RegisterGlobalFilters(GlobalFilterCollection filters) 4 { 5 //若是自定义异常过滤器,须要把默认的异常过滤器给注释掉 6 //filters.Add(new HandleErrorAttribute()); 7 8 //自定义异常过滤器 9 filters.Add(new MyException()); 10 11 //全局注册身份验证、行为、结果过滤器 12 //filters.Add(new MyAuthorize()); 13 //filters.Add(new MyAction()); 14 //filters.Add(new MyResult()); 15 16 //全局注册登陆验证(暂时注释,使用的时候要打开) 17 //filters.Add(new CheckLogin()); 18 } 19 }
四. 结合实际案例进行代码测试
1. 测试过滤器的执行顺序
将上面的身份验证过滤器、行为过滤器、结果过滤器以特性的形式做用于Action上,经过断点监控或者查看最后的输出结果:
结果:
符合:OnAuthorization→OnActionExecuting-> Action方法执行 ->OnActionExecuted->OnResultExecuting/ -> Render View() (页面渲染加载)->OnResultExecuted() 这一顺序。
2. 全局捕获异常,记录错误日志案例
步骤1:编写异常过滤器,经过 var errorMsg = filterContext.Exception; 获取异常信息,能够写入文本、存入数据库、或者是Log4Net错误日志框架进行处理。代码在上面。
步骤2:在web.config中为system.web添加<customErrors mode="On" />节点。
步骤3:添加到全局注册文件中进行捕获。
步骤4:在自定义的异常过滤器中添加断点,而且本身制造一个错误。
捕获到错误,进行页面跳转。
3. 登陆验证案例
业务背景:在90%以上的Web系统中,不少页面都是登陆成功之后才能看到的,固然也有不少页面不须要登陆,对于须要登陆才能看到的页面,即便你知道了访问地址,也是不能访问的,会退出到登陆页面提示让你登陆,对于不须要登陆的页面经过URL地址能够直接访问。
分析:针对以上背景,过滤器对于大部分Action是须要过滤的,须要作登陆验证,对于一小部分是不须要过滤的。
解决思路:
①:自定义一个身份验证过滤器,进行全局注册。
②:自定义一个Skip特性,将该特性加到不须要身份验证的Action上。
③:重点说一下身份证过滤器中的逻辑:
a. 先判断该Action上是否又Skip特性,若是有,中止不继续执行;若是没有,继续下面的验证逻辑。
b. 从Session中或Redis中读取当前用户,查看是否为空,若是为空,代表没有登陆,返回到登陆页面;若是不为空,验证经过,进行后面的业务逻辑。
代码段以下:
1 /// <summary> 2 /// 校验系统是否登陆的过滤器 3 /// 使用身份验证过滤器进行编写 4 /// </summary> 5 public class CheckLogin: AuthorizeAttribute 6 { 7 public override void OnAuthorization(AuthorizationContext filterContext) 8 { 9 //1. 校验是否标记跨过登陆验证 10 if (filterContext.ActionDescriptor.IsDefined(typeof(skipAttribute), true) 11 || filterContext.ActionDescriptor.ControllerDescriptor.IsDefined(typeof(skipAttribute), true)) 12 { 13 //表示该方法或控制器跨过登陆验证 14 return; 15 } 16 //2. 校验是否登陆 17 //可使Session或数据库或nosql 18 //这里只是测试,全部通通当作没有登陆来处理 19 var sessionUser = HttpContext.Current.Session["CurrentUser"];//使用session 20 if (sessionUser == null) 21 { 22 HttpContext.Current.Session["CurrentUrl"] = filterContext.RequestContext.HttpContext.Request.RawUrl; 23 //若是没有登陆,则跳转到错误页面 24 filterContext.Result = new RedirectResult("/error.html"); 25 } 26 } 27 }