1、过滤器(Filters)就是向请求处理管道中注入额外的逻辑。提供了一个简单而优雅的方式来实现横切关注点。
2、所谓的过滤器(Filters),MVC 框架里面的过滤器完全不同于 ASP.NET 平台里面的 Request.Filters 和 Response.Filter 对象,它们主要是实现请求和响应流的传输。通常我们所说的过滤器是指 MVC 框架里面的过滤器。
3、过滤器可以注入一些代码逻辑到请求处理管道中,是基于 C# 的 Attribute 的实现。当负责调用 Action 的类 ControllerActionInvoker 在调用执行 Action 的时候会检查 Action 上面的 Attribute 并查看这些 Attribute 是否实现了指定的接口,以便进行额外的代码注入处理
假设你做了一个小项目,其中某个功能是操作管理用户信息模块,有这样一个需求,对用户信息管理必须是已通过认证的用户才能操作,我们可以在每一个 Action 方法里面检查认证请求,如下所示:
- 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
- }
通过上面的代码,可以发现使用这种方式检查请求认证有许多重复的地方,这也就是为什么要使用过滤器的原因,使用过滤器可以实现相同的效果。如下所示:
- 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),如果实现的话,那么在请求处理管道适当的位置,该特性定义的方法会被调用。
ActionFilterAttribute 类既实现了 IactionFilter 接口,也实现 IResultFilter 接口。这是一个抽象类,它要求你必须提供一个实现。AuthorizeAttribute 和 HandleErrorAttribute 类,则包含了一些有用的特性,并且可以不必创建派生类进行使用。
应用: 过滤器可以被应用到控制器上也可以用到 Action 方法上,应用到控制上时,表示所有的 Action 方法都有了这个过滤器,并且可以混合使用,或多次使用,如下所示:
- 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 }
应用方式:特性的方式,如上面代码所示。
执行顺序:相同类型过滤器,执行顺序靠近方法的先执行,不同类型的过滤器一般执行顺序为【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 框架内置基本的过滤器和如何自定义过滤器及应用。
来源: http://www.cnblogs.com/SeeYouBug/p/6446100.html