一、过滤器(Filters)就是向请求处理管道中注入额外的逻辑。提供了一个简单而优雅的方式来实现横切关注点。html
二、所谓的过滤器(Filters),MVC框架里面的过滤器彻底不一样于ASP.NET平台里面的Request.Filters和Response.Filter对象,它们主要是实现请求和响应流的传输。一般咱们所说的过滤器是指MVC框架里面的过滤器。安全
三、过滤器能够注入一些代码逻辑到请求处理管道中,是基于C#的Attribute的实现。当负责调用Action的类ControllerActionInvoker在调用执行Action的时候会检查Action上面的Attribute并查看这些Attribute是否实现了指定的接口,以便进行额外的代码注入处理框架
假设你作了一个小项目,其中某个功能是操做管理用户信息模块,有这样一个需求,对用户信息管理必须是已经过认证的用户才能操做,咱们能够在每个Action方法里面检查认证请求,以下所示:asp.net
1 using MvcFilterDmo.Core; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web; 6 using System.Web.Mvc; 7 using System.Web.Security; 8 9 namespace MvcFilterDmo.Controllers 10 { 11 public class HomeController : Controller 12 { 13 public ActionResult Index() 14 { 15 if (!Request.IsAuthenticated) 16 { 17 FormsAuthentication.RedirectToLoginPage(); 18 } 19 //操做部分... 20 return View(); 21 } 22 public ActionResult Insert() 23 { 24 if (!Request.IsAuthenticated) 25 { 26 FormsAuthentication.RedirectToLoginPage(); 27 } 28 //操做部分... 29 return View(); 30 } 31 public ActionResult Update() 32 { 33 if (!Request.IsAuthenticated) 34 { 35 FormsAuthentication.RedirectToLoginPage(); 36 } 37 //操做部分... 38 return View(); 39 } 40 public ActionResult Delete() 41 { 42 if (!Request.IsAuthenticated) 43 { 44 FormsAuthentication.RedirectToLoginPage(); 45 } 46 //操做部分... 47 return View(); 48 } 49 //其余Action操做方法 50 //... 51 } 52 }
经过上面的代码,能够发现使用这种方式检查请求认证有许多重复的地方,这也就是为何要使用过滤器的缘由,使用过滤器能够实现相同的效果。以下所示:ide
1 using MvcFilterDmo.Core; 2 using System; 3 using System.Collections.Generic; 4 using System.Linq; 5 using System.Web; 6 using System.Web.Mvc; 7 using System.Web.Security; 8 9 namespace MvcFilterDmo.Controllers 10 { 11 [Authorize] 12 public class HomeController : Controller 13 { 14 public ActionResult Index() 15 { 16 //操做部分... 17 return View(); 18 } 19 public ActionResult Insert() 20 { 21 //操做部分... 22 return View(); 23 } 24 public ActionResult Edit() 25 { 26 //操做部分... 27 return View(); 28 } 29 public ActionResult Delete() 30 { 31 //操做部分... 32 return View(); 33 } 34 //其余Action操做方法 35 //... 36 } 37 }
过滤器是.NET里面的特性(Attributes),它提供了添加到请求处理管道的额外方法。这里使用Authorize过滤器能够实现一样的效果,不过代码就显然比以前更加简洁优雅。测试
过滤器实现的机制:在MVC框架调用一个Action以前,它会检查方法的定义中是否实现了特性(Attributes),若是实现的话,那么在请求处理管道适当的位置,该特性定义的方法会被调用。ui
ActionFilterAttribute类既实现了IactionFilter接口,也实现IResultFilter接口。这是一个抽象类,它要求你必须提供一个实现。AuthorizeAttribute和HandleErrorAttribute类,则包含了一些有用的特性,而且能够没必要建立派生类进行使用。this
应用: 过滤器能够被应用到控制器上也能够用到Action方法上,应用到控制上时,表示全部的Action方法都有了这个过滤器,而且能够混合使用,或屡次使用,以下所示:spa
1 [A] //表示全部的Action方法都会应用A过滤器 2 Public class DemoController:Controller 3 { 4 [B]//B,C过滤器只做用于此Action方法,但它也会有A过滤器的应用效果 5 [C] 6 Public ActionResult Index() 7 { 8 //操做部分... 9 return View(); 10 } 11 }
应用方式:特性的方式,如上面代码所示。.net
执行顺序:相同类型过滤器,执行顺序靠近方法的先执行,不一样类型的过滤器通常执行顺序为【authorize--->action--->actionResult】至于异常过滤器不分前后,只要抛出异常时就会执行异常过滤器。若是要调整执行顺序,能够经过调整Order方法值大小来控制执行顺序,值越小,越先执行。下图是Action/Result过滤器应用的执行顺序图
(1)、相同类型过滤器应用示例:两个自定义Action过滤器MyFirstFilter,MyThreeFilter应用到同一个Action方法Index上。
Three控制器代码以下:
MyFirstFilter 代码以下:
MyThreeFilter代码以下:
运行结果以下:
(2)、不一样类型过滤器应用示例:有一个自定义Action过滤器MyFirstFilter,有一个自定义Result过滤器MySecondFilter,应用到同一个Action方法Index上。
Three控制器代码以下:
MyFirstFilter 代码以下:
MySecondFilter代码以下:
运行结果以下:
看完上面的解释,可能你如今对这些过滤器的执行顺序,以及如何自定义过滤器还不明白,没关系,下面咱们会逐一介绍这几个基本的过滤器的使用,以及如何自定义过滤器。
全部实现了IAuthorizationFilter接口的均可以称之为受权过滤器:其定义以下:
1 public interface IAuthorizationFilter 2 { 3 void OnAuthorization(AuthorizationContext filterContext); 4 }
因为MVC框架系统自带的AuthorizeAttribute实现有一些突出的功能,而这种牵涉到安全的代码必定要谨慎的编写,因此通常咱们不会直接实现这个接口,而是去继承AuthorizeAttribute这个类,并重写其AuthorizeCore方法,签名为: bool AuthorizeCore(HttpContextBase httpContext) 而处理受权失败的时候,能够重写其HandleUnauthorizedRequest方法,其签名为: void HandleUnauthorizedRequest(AuthorizationContext context) 。注意:验证与受权是两回事,验证发生在受权以前。
默认的受权过滤器已经有了验证的功能,其验证的机理是利用Asp.net平台自带的验证机制,如表单验证和Windows验证。除了验证功能,它自己还有受权的功能。受权过滤器是全部过滤器中最先运行的。
通过Route到达了控制器的时候,在调用Action以前,MVC框架会检测在相关的Action上是否有受权过滤器,若是有会调用OnAuthorization方法,若是此方法批准了请求,才会调用相应的Action。
使用受权过滤器几种状况以下:
1.直接在Action上或者控制器上加Authorize,表示启用了验证,但不牵涉到受权。
2.添加Authorize(Users=“a,b”)],表示启用了验证,而且也启用了受权,只有a或者b用户能访问此控制器。
3.当添加Authorize(Roles=“admin,Member”)]时的步骤以下:
---利用asp.net自带的角色提供者,或者实现本身的角色提供者,实现本身的角色提供者时,只须要集成RoleProvider类型,并实现其中的全部方法或部分方法,最好实现全部方法。
---在Web程序的根目录的Web.config文件中配置角色管理者。
---在适当的Action中利用Roles类型来访问本身建立的RoleProvider中的相关方法。
使用内置的受权过滤器
MVC框架内置的受权过滤器AuthorizeAttribute,它容许咱们使用这个类的两个公共属性来指定受权策略,以下所示:
Users和Roles二者是而且的关系,例如Users=“a,b,c”,Roles=“admin”,表示用户是a,b,c 其中一个而且是Admin角色才能访问。
建立自定义的受权过滤器
方式一:直接实现IAuthorizationFilter接口,但不推荐这样作,由于牵涉到安全方面的代码。
方式二:继承AuthorizeAttribute这个类,并重写其AuthorizeCore方法,签名为: bool AuthorizeCore(HttpContextBase httpContext),代码以下所示:
1 public class MyAuthorizeAttribute : AuthorizeAttribute 2 { 3 private string[] allowedUsers; 4 public MyAuthorizeAttribute(params string[] users) 5 { 6 allowedUsers = new string[] { "admin", "user1", "xf" }; 7 } 8 9 protected override bool AuthorizeCore(HttpContextBase httpContext) 10 { 11 return httpContext.Request.IsAuthenticated &&allowedUsers.Contains(httpContext.User.Identity.Name, 12 StringComparer.InvariantCultureIgnoreCase); 13 } 14 }
动做过滤器是能够以用于任何目的的多用途过滤器,建立自定义动做过滤器须要实现IActionFilter接口,该接口代码以下所示:
该接口定义了两个方法,MVC框架在调用动做方法以前,会调用OnActionExecting方法。在调用动做方法以后,则会调用OnActionExecuted方法。
实现OnActionExecting方法
参数ActionExecutingContext对象继承于ControllerContext,其中的2个属性:
ActionDescriptor:提供了关于Action方法的相关信息
Result:类型为ActionResult,经过给这个属性设置一个非null的值就能够取消这个请求。
咱们能够用过滤器来取消一个请求,经过设置Result属性便可。代码以下所示:
1 public class MyActionFilterAttribute : FilterAttribute, IActionFilter 2 { 3 public void OnActionExecuting(ActionExecutingContext filterContext) 4 { 5 if(filterContext.HttpContext.Request.IsLocal) 6 { 7 filterContext.Result = new HttpNotFoundResult(); 8 } 9 } 10 public void OnActionExecuted(ActionExecutedContext filterContext) 11 { 12 //未作实现 13 } 14 }
这个例子经过用OnActionExecuting方法检查请求是否来自本地机器,若是是,编队用户返回一个“404”未找到的响应。运行结果以下图:
实现OnActionExecuted方法
咱们也能够经过OnActionExecuted方法来执行一些跨越动做方法的任务,下面这个例子是计算动做方法运行的时间,代码以下:
1 public class MyActionFilterAttribute : FilterAttribute, IActionFilter 2 { 3 private Stopwatch timer; 4 public void OnActionExecuting(ActionExecutingContext filterContext) 5 { 6 timer = Stopwatch.StartNew(); 7 } 8 public void OnActionExecuted(ActionExecutedContext filterContext) 9 { 10 timer.Stop(); 11 if (filterContext.Exception == null) 12 { 13 filterContext.HttpContext.Response.Write( 14 string.Format("动做方法延迟的时间: {0}", 15 timer.Elapsed.TotalSeconds)); 16 } 17 } 18 } 19 }
咱们将自定义的动做过滤器MyActionFilter应用到HomeController的Index方法上,运行结果以下:
结果过滤器是多用途的过滤器,他会对动做方法所产生结果进行操做,结果过滤器实现IResultFilter接口,建立自定义结果过滤器须要现IResultFilter接口,该接口代码以下所示:
当结果过滤器运用于一个动做方法时,会在动做方法返回动做结果以前,调用OnResultExecuting方法,在返回动做结果以后,会调用OnResultExecuted方法。下面这个例子是计算动做方法返回结果运行的时间,代码以下:
1 public class MyResultFilterAttribute : FilterAttribute, IResultFilter 2 { 3 private Stopwatch timer; 4 public void OnResultExecuting(ResultExecutingContext filterContext) 5 { 6 timer = Stopwatch.StartNew(); 7 } 8 public void OnResultExecuted(ResultExecutedContext filterContext) 9 { 10 timer.Stop(); 11 filterContext.HttpContext.Response.Write(string.Format("结果执行延迟时间: {0}", timer.Elapsed.TotalSeconds)); 12 } 13 }
咱们将自定义的结果过滤器MyResultFilter应用到HomeController的Index方法上,运行结果以下:
须要注意的是:动做过滤器是运行在页面输出以前,结果过滤器是运行在页面输出以后。
异常过滤器只有在调用一个动做方法而抛出未处理的异常才会运行,这种异常来自如下位置:
A、另外一种过滤器(受权、动做、或结果过滤器)。
B、动做方法自己。
C、当动做结果被执行时。
使用内置的异常过滤器
HandleErrorAttribute(处理程序错误特性),它是MVC内嵌的异常过滤器,有如下3个重要的属性:
1.ExceptionType:类型为Type,表示但愿被此过滤器处理的异常类型,包括其子类型,默认值为System.Exception
2.View:类型为string,表示此过滤器呈递的视图页面,默认值为Error
3.Master:呈递的视图页的母板页,若是不指定,视图会用其默认的母版页
内嵌的HandleErrorException只有在配置文件Web.config中配置的CustomError 的mode设置为on的时候才生效(其默认模式为RemoteOnly),以下图所示:
此过滤器还会给视图传递一个HandleErrorInfo类型的对象给视图,以便视图能够显示一些额外的关于错误的信息。下面是使用异常过滤器的示例。
应用到Index动做方法上:
在Views/Shared文件夹下添加一个显示异常信息的视图页SpecialError.cshtml,页面代码以下:
1 @model HandleErrorInfo 2 <!DOCTYPE html> 3 <html> 4 <head> 5 <meta name="viewport" content="width=device-width" /> 6 <title>SpecialError</title> 7 </head> 8 <body> 9 <div> 10 <p> 11 There was a<b>@Model.Exception.GetType().Name</b> 12 while rendering<b>@Model.ControllerName</b>'s 13 <b>@Model.ActionName</b> action 14 </p> 15 </div> 16 </body> 17 </html>
运行结果以下:
建立自定义的异常过滤器
若是咱们对异常过滤器有特殊的需求,能够经过自定义的异常过滤器来完成,建立自定义异常过滤器必须实现IExceptionFilter接口,该接口代码以下:
当一个未知处理异常发生时,OnException方法会被调用。该方法的传递一个ExceptionContext对象,派生于ControllerContext类,定义了一些额外的过滤器专有属性以下表所示:
抛出的异常经过Exception属性是能够访问的。经过把ExceptionHandled属性设置为true,一个异常过滤器能够报告它已经处理了该异常,应用于一个动做的全部异常过滤器都会被调用。
须要注意的是:若是一个动做方法的全部异常过滤器均为把ExceptionHandled属性设置为true,MVC框架将使用默认的ASP.NET异常处理程序。
Result属性有异常过滤器使用,以告诉MVC框架要作什么,异常过滤器的两个主要应用是记录该异常到日志,并把适当的消息显示给用户。下面的代码将演示经过建立一个自定义的异常过滤器,当一个特定的钟类的未处理异常出现时,把该用户重定向到一个指定的错误页面。
1 public class MyExectionAttribute:FilterAttribute,IExceptionFilter 2 { 3 public void OnException(ExceptionContext filterContext) 4 { 5 if(!filterContext.ExceptionHandled&& 6 filterContext.Exception is NullReferenceException) 7 { 8 filterContext.Result = new RedirectResult("~/Content/SpecialErrorPage.html"); 9 filterContext.ExceptionHandled = true; 10 } 11 } 12 }
而后在项目根目录添加一个名为Content的文件夹,在该文件夹下建立SpeciErrorPage.html文件,当异常被处理时,将以这个错误页面显示个用户。该页面代码以下:
1 <!DOCTYPE html> 2 <html xmlns="http://www.w3.org/1999/xhtml"> 3 <head> 4 <meta http-equiv="Content-Type" content="text/html; charset=utf-8"/> 5 <title></title> 6 </head> 7 <body> 8 <h1>Sorry</h1> 9 <p>this is a Excetption test</p> 10 There was aNullReferenceException while renderingHome's Index action 11 </body> 12 </html>
在控制器中应用MyExection异常过滤器,并主动让其抛出一个空引用异常,以便测试。
1 public class HomeController : Controller 2 { 3 [MyExection] 4 public ActionResult Index() 5 { 6 throw new NullReferenceException(); 7 } 8 }
运行结果以下:
总结:本文章简单总结了对过滤器的理解以及如何使用MVC框架内置基本的过滤器和如何自定义过滤器及应用。