Action Filtering in ASP.NET MVC Applications
Base Interview
MVC中一个重要的功能——Filter,我们通过研究源代码来了解Filter的原理,以及AOP 模式和各种Filter的执行。最重要的是大家通过理解Filter的代码,明白Filter的机制,从而对Filter有一个灵活的运用。
比如你在Action上使用的每一个 [Attribute]大都是Filter,mvc提供四种类型的 Filter:IActionFilter,IAuthorizationFilter,IExceptionFilter,IResultFilter,这四种Filter足以我们所要实现的功能了,还提供了几个现成的可以使用的Filter:OutputCacheAttribute、 HandleErrorAttribute、AuthorizeAttribute。我们知道Filter是横切在Action上的,所以每次执行一个 Action,就会牵扯到这个Action的执行,不同类型的Filter提供不同类型的操作,Filter的设计就是一种AOP设计模式,所以这种方式十分灵活。
AuthorizeAttribute | 介绍如何使用 Authorize 特性控制对操作方法的访问。 |
OutputCacheAttribute | 介绍如何使用 OutputCache 特性缓存操作方法的输出结果。 |
HandleErrorAttribute | 解释如何向 MVC 应用程序添加自定义操作筛选器。 |
对于四种类型的Filter,各自有不同的方法,并以此来实现不同的功能,我们可以发挥自己的创造力,定制一个自己的Filter,来完成自己想要的功能。一般的Filter应用的场景有:缓存、验证、异常,以及处理Action上下文等,要想实现自己的Filter 只要继承相应的接口并重写接口的方法就可以了。
- 授权筛选器,用于做出关于是否执行操作方法(如执行身份验证或验证请求的属性)的安全决策。 AuthorizeAttribute 类是授权筛选器的一个示例。
- 操作筛选器,用于包装操作方法的执行操作。此筛选器可以执行其他处理,如向操作方法提供额外数据、检查返回值或取消执行操作方法。
- 结果筛选器,用于包装 ActionResult 对象的执行操作。此筛选器可以对结果执行其他处理,如修改 HTTP 响应。 OutputCacheAttribute 类是结果筛选器的一个示例。
- 异常筛选器,如果在操作方法中某处引发了未经处理的异常,则从授权筛选器开始执行,执行结果筛选器后结束。异常筛选器可用于执行诸如日志记录或显示错误页之类的任务。 HandleErrorAttribute 类是异常筛选器的一个示例
Why Use Filter
我们知道Filter是一种AOP模式,也就是说它提供我们可以对一系列操作进行横切干扰的手段,而且它解耦了依赖关系。想想这么一种场景,在传统的WebForm中,某一个页面的访问必须去验证当前用户是否符合某一个要求,比如已登录,这时,我们需要获取请求的上下文,分析当前用户的行为状态,从而得到这个条件是否成立,如果我们有很多这种页面,比如所有的后台管理页面需要管理员的登录,我们是不是每一页都写一个这样的验证呢?我们当然不会这么蠢,我们会聪明的把验证的逻辑封装成一个验证类,然后从各个页面调用验证类的验证逻辑,从而得知当前用户是否通过验证。
这样还是不够好,因为我们不得不在每一处反复写我们的调用验证类的验证代码,显然这些工作是重复的,因为那个时候还没有AOP的概念,所以我们不得不多付出点代价。
现在有了mvc,它理所应当为我们提供一个AOP的框架,而Filter就是这么一种模式,它的出现容许我们以一种更为简单的方式来实现类似的功能,在使用的时候我们只需要像该某个商品贴标签式的方式给Action“贴”上一个Attribute就行了,一切搞定,没有一点多于的代码。
Use default Filter
操作筛选器的某些可能用途包括:
- 日志记录,目的是跟踪用户交互。
- “反图像攫取”,用于防止在自己网站之外的网页中加载图像。
- 爬网程序筛选,用于根据浏览器用户代理来更改应用程序行为。
- 本地化,用于设定区域设置。
- 动态操作,用于将操作注入到控制器中。
操作筛选器是以从 ActionFilterAttribute 中继承的特性类的形式实现的。 ActionFilterAttribute 是一个具有以下四个可以重写的虚拟方法的抽象类:OnActionExecuting、OnActionExecuted、OnResultExecuting 和 OnResultExecuted。若要实现操作筛选器,您至少必须重写这些方法之一。
ASP.NET MVC 框架将先调用操作筛选器的 OnActionExecuting 方法,然后再调用以操作筛选器特性标记的任意操作方法。同样,该框架将在操作方法完成后调用 OnActionExecuted 方法。
调用 OnResultExecuting 方法后,要立即调用您的操作返回的 ActionResult 实例。执行结果后,紧接着就要调用 OnResultExecuted 方法。这些方法对于执行日志记录、缓存输出结果之类的操作非常有用。
下面演示自带的Filter 如OutputCacheAttribute 类
配置缓存文件如下
<system.web> <caching> <outputCacheSettings> <outputCacheProfiles> <add name="MyProfile" duration="60" varyByParam="*" /> </outputCacheProfiles> </outputCacheSettings> </caching> </system.web>
标记制作器中的Action
[OutputCache(CacheProfile = "MyProfile", Duration = 10)] public ActionResult About() { ViewData["Message"] = "This page was cached at " + DateTime.Now; return View(); }
Creating a custom action filter
下面的示例演示一个简单的操作筛选器,该操作筛选器记录调用操作方法之前和之后的跟踪消息
注意: 按照约定,操作筛选器特性的名称应以“Attribute”作为结尾,LogFilterAttribute 或 OutputFilterAttribute。如创建一个日志异常的Filter
public class LoggingFilterAttribute : ActionFilterAttribute { public override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.HttpContext.Trace.Write("(Logging Filter)Action Executing: " + filterContext.ActionDescriptor.ActionName); base.OnActionExecuting(filterContext); } public override void OnActionExecuted(ActionExecutedContext filterContext) { if (filterContext.Exception != null) filterContext.HttpContext.Trace.Write("(Logging Filter)Exception thrown"); base.OnActionExecuted(filterContext); } }
使用也很简单 ,用操作筛选器特性标记的操作方法的控制器
[LoggingFilter] public ActionResult Index() { ViewBag.Message = "修改此模板以快速启动你的 ASP.NET MVC 应用程序。"; return View(); } [LoggingFilter] public ActionResult About() { ViewBag.Message = "你的应用程序说明页。"; return View(); }
或者在控制器中重写
protected override void OnActionExecuting(ActionExecutingContext filterContext) { filterContext.HttpContext.Trace.Write("(Controller)Action Executing: " + filterContext.ActionDescriptor.ActionName); base.OnActionExecuting(filterContext); } [NonAction] protected override void OnActionExecuted(ActionExecutedContext filterContext) { if (filterContext.Exception != null) filterContext.HttpContext.Trace.Write("(Controller)Exception thrown"); base.OnActionExecuted(filterContext); } [LoggingFilter] public ActionResult Index() { ViewBag.Message = "修改此模板以快速启动你的 ASP.NET MVC 应用程序。"; return View(); } [LoggingFilter] public ActionResult About() { ViewBag.Message = "你的应用程序说明页。"; return View(); }
或者注册全局的Filter,在App_Start 中的 FilterConfig.cs文件中(MVC4)注册我们的LoggingFilterAttribute
public static void RegisterGlobalFilters(GlobalFilterCollection filters) { filters.Add(new LoggingFilterAttribute()); filters.Add(new HandleErrorAttribute()); }
我们也可以看下HandleErrorAttribute类 F12
// 摘要: // 表示一个特性,该特性用于处理由操作方法引发的异常。 [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true, AllowMultiple = true)] public class HandleErrorAttribute : FilterAttribute, IExceptionFilter { // 摘要: // 初始化 System.Web.Mvc.HandleErrorAttribute 类的新实例。 public HandleErrorAttribute(); // 摘要: // 获取或设置异常的类型。 // // 返回结果: // 异常的类型。 public Type ExceptionType { get; set; } // // 摘要: // 获取或设置用于显示异常信息的母版视图。 // // 返回结果: // 母版视图。 public string Master { get; set; } // // 摘要: // 获取此特性的唯一标识符。 // // 返回结果: // 此特性的唯一标识符。 public override object TypeId { get; } // // 摘要: // 获取或设置用于显示异常信息的页视图。 // // 返回结果: // 页视图。 public string View { get; set; } // 摘要: // 在发生异常时调用。 // // 参数: // filterContext: // 操作-筛选器上下文。 // // 异常: // System.ArgumentNullException: // filterContext 参数为 null。 public virtual void OnException(ExceptionContext filterContext); }
如我看可以看到 Master的额外参数,我们可以在控制器中如下标记
[HandleErrorAttribute(Master="我是参数 但不是模板页")] public ActionResult Contact() { ViewBag.Message = "你的联系方式页。"; return View(); }
我们也可以添加我们想要的值,如添加一个属性在 (public string Message { get; set; } )在LoggingFilterAttribute 类
我们也可以把该filter特性标记上面的方法中,如:
[LoggingFilterAttribute (Message = "action")] public class IndexController : Controller {…}
全局中我们可以通过下面方式获取控制器与action的名称
filterContext.RouteData.GetRequiredString("Controller") + filterContext.RouteData.GetRequiredString("action");
Summary
在本篇文章中,讲述的内容非常有限,主要讨论了怎么获取Filter的,以及Controller与各种 Filter的关系,还有Filter的4种类型,但是我们没有具体讨论4种类型Filter的方法。还有最终要的就是大家通过这个探究这个过程,真正了理解Filter的设计思想和AOP在mvc中的运用,以及Filter的功能强大之处。
Link:
http://msdn.microsoft.com/zh-cn/library/dd410209(v=vs.100).aspx
http://weblogs.asp.net/rashid/archive/2010/05/27/mvcextensions-actionfilter.aspx