Action Filters以及自定义OutputCache ActionFilterAttribute事件发生次序
理解 Action Filters
Action filter 是能够应用于 controller action --或整个controller的一个特性,它们的基类为System.Web.Mvc.FilterAttribute 。它限定了action执行的方式。ASP.NET MVC框架包含数个action filters。
HandleError – 这个action 过滤器处理controller action执行时出现的错误。
OutputCache – 这个action 过滤器将 controller action的输出缓存一段制定的时间 .
Authorize – 这个action 过滤器使你能够限制特定的用户或角色的访问.
使用Action Filter
action filter是一个特性. 你能够应用大部分的action filters 在单个的controller action 或者整个controller上.
例如下面的Data controller有一个返回当前时间的Index()方法.这个action拥有OutputCache action filter. 这个过滤器导致由action返回的值能够缓存10秒钟.
VaryByParam 属性使用的设置不建议通过设置“*”的值来使用所有参数进行区分。这可能会导致缓存溢出。
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->public class DataController : Controller { // // GET: /Data/ [OutputCache(Duration = 20,VaryByParam ="")] public string Index() { return DateTime.Now.ToString(); } }
如果你重复调用Index() action(不断刷新当前页面), 那么你将看到当前的内容在Duration = 20秒内是不变的.
一个单一的action filter – OutputCache action filter – 被应用于Index() 方法. 同样,你可以应用多个action filters 在同一个action上.
不同类型的Filters
ASP.NET MVC框架支持多种不同类型的过滤器:
Authorization filters – 实现IAuthorizationFilter 特性.
Action filters – 实现IActionFilter 特性.
Result filters – 实现IResultFilter 特性.
Exception filters –实现IExceptionFilter 特性.
Filters 按照上面列出的顺序执行。例如, authorization filters 总是在action filters之前执行,exception filters在所有其他类型的filter之后执行.
ActionFilterAttribute 基类
为了使你能够更加容易的实现自定义的action filter, ASP.NET MVC框架包含一个ActionFilterAttribute 基类. 这个类实现了IActionFilter 与IResultFilter 接口,并且继承了Filter 类。
ActionFilterAttribute 基类拥有以下可以重载的方法:
OnActionExecuting在action method调用前发生。
OnActionExecuted在action method调用后发生, 但是在result执行前发生 (在 view 呈现前)
OnResultExecuting在result执行前发生(在view 呈现前)
OnResultExecuted 在result执行后发生(在view 呈现后)
创建一个ASP.NET MVC OutputCache ActionFilterAttribute
在每一个web应用程序中, 有的情况下,你想在一段时间内缓存一个具体的页面HTML输出,因为相关的数据和处理并不是总是变化。这种缓存的响应是储存在服务器的内存中。因为没有必要的额外处理,它提供了非常快速的响应。使用经典的ASP.NET,你可以在.aspx页面上使用OutputCache指令,它告诉ASP.NET运行时在某一特定的时间段内来缓存响应数据。缓存可随参数而改变,这将导致产生依赖于参数的不同缓存响应。作为一个额外的功能,还可以发送一些HTTP头到客户端。在一段时间以内,客户端从浏览器缓存中加载页面。大的优势是你的web服务器将接收更少的客户端要求,因为他们仅仅是使用他们自己的缓存。
使用ASP.NET MVC 框架, 简单的指定OutputCache 指令并不能达到理想的效果. 幸好, ActionFilterAttribute让你能够在 controller action执行的前后运行代码.
让我们使用类似的方法来创建OutputCacheActionFilterAttribute。
[OutputCache(Duration = 60, VaryByParam = "*", CachePolicy = CachePolicy.Server)] public ActionResult Index() { // ... }
我们将使用命名为CachePolicy的枚举类型来指定OutputCache 特性应怎样以及在哪里进行缓存:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->public enum CachePolicy { NoCache = 0, Client = 1, Server = 2, ClientAndServer = 3 }
1.实现client-side缓存
事实上,这是很容易的。在view呈现前,我们将增加一些HTTP头到响应流。网页浏览器将获得这些头部,并且通过使用正确的缓存设置来回应请求。如果我们设置duration为60,浏览器将首页缓存一分钟。
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->using System.Web.Mvc; namespace MVCActionFilters.Web.Models { public class OutputCache:System.Web.Mvc.ActionFilterAttribute { public int Duration { get; set; } public CachePolicy CachePolicy { get; set; } public override void OnActionExecuted(ActionExecutedContext filterContext) { if (CachePolicy == CachePolicy.Client || CachePolicy == CachePolicy.ClientAndServer) { if (Duration <= 0) return; //用于设置特定于缓存的 HTTP 标头以及用于控制 ASP.NET 页输出缓存 HttpCachePolicyBase cache = filterContext.HttpContext.Response.Cache; TimeSpan cacheDuration = TimeSpan.FromSeconds(Duration); cache.SetCacheability(HttpCacheability.Public); cache.SetExpires(DateTime.Now.Add(cacheDuration)); cache.SetMaxAge(cacheDuration); cache.AppendCacheExtension("must-revalidate, proxy-revalidate"); } } } }
2. 实现server-side缓存
Server-side 缓存有一点难度. 首要的,在输出缓存系统中,我们将不得不准备HTTP 响应为可读的。为了这样做,我们首先保存当前的HTTP context到类的一个变量中. 然后, 我们创建一个新的httpcontext ,通过它将数据写入StringWriter,同时允许读操作可以发生:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ -->existingContext = System.Web.HttpContext.Current;//保存当前的HTTP context到类的一个变量中 writer = new StringWriter(); HttpResponse response = new HttpResponse(writer); HttpContext context = new HttpContext(existingContext.Request, response) { User = existingContext.User }; System.Web.HttpContext.Current = context;
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> public override void OnResultExecuting(ResultExecutingContext filterContext) { if (CachePolicy == CachePolicy.Server || CachePolicy == CachePolicy.ClientAndServer) { //获取缓存实例 cache = filterContext.HttpContext.Cache; // 获取缓存数据 object cachedData = cache.Get(GenerateKey(filterContext)); if (cachedData != null) { // 返回缓存数据 cacheHit = true; filterContext.HttpContext.Response.Write(cachedData); filterContext.Cancel = true; } else { //重新设置缓存数据 existingContext = System.Web.HttpContext.Current; writer = new StringWriter(); HttpResponse response = new HttpResponse(writer); HttpContext context = new HttpContext(existingContext.Request, response) { User = existingContext.User }; foreach (var key in existingContext.Items.Keys) { context.Items[key] = existingContext.Items[key]; } System.Web.HttpContext.Current = context; } } }
利用该代码,我们能从高速缓存中检索现有项,并设置了HTTP响应能够被读取。在视图呈现之后,将数据存储在高速缓存中:
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> public override void OnResultExecuted(ResultExecutedContext filterContext) { // 服务器端缓存? if (CachePolicy == CachePolicy.Server || CachePolicy == CachePolicy.ClientAndServer) { if (!cacheHit) { // 存储原有的context System.Web.HttpContext.Current = existingContext; // 返回呈现的数据 existingContext.Response.Write(writer.ToString()); //增加数据到缓存 cache.Add( GenerateKey(filterContext), writer.ToString(), null, DateTime.Now.AddSeconds(Duration), Cache.NoSlidingExpiration, CacheItemPriority.Normal, null); } } }
你现在注意到添加了一个VaryByParam到 OutputCache ActionFilterAttribute。当缓存server-side时,我可以通过传入的参数来改变缓存存储。这个GenerateKey方法会产生一个依赖于controller,action和VaryByParam的键。
Code highlighting produced by Actipro CodeHighlighter (freeware) http://www.CodeHighlighter.com/ --> private string GenerateKey(ControllerContext filterContext) { StringBuilder cacheKey = new StringBuilder(); // Controller + action cacheKey.Append(filterContext.Controller.GetType().FullName); if (filterContext.RouteData.Values.ContainsKey("action")) { cacheKey.Append("_"); cacheKey.Append(filterContext.RouteData.Values["action"].ToString()); } // Variation by parameters List<string> varyByParam = VaryByParam.Split(';').ToList(); if (!string.IsNullOrEmpty(VaryByParam)) { foreach (KeyValuePair<string, object> pair in filterContext.RouteData.Values) { if (VaryByParam == "*" || varyByParam.Contains(pair.Key)) { cacheKey.Append("_"); cacheKey.Append(pair.Key); cacheKey.Append("="); cacheKey.Append(pair.Value.ToString()); } } } return cacheKey.ToString(); }
现在你可以增加 OutputCache attribute 到应用程序的任何一个controller 与controller action中 。
[MVCActionFilters.Web.Common.OutputCache(Duration = 20, VaryByParam = "*",CachePolicy=Common.CachePolicy.Client)] public string Cache() { return DateTime.Now.ToString(); }
设置CachePolicy为Common.CachePolicy.Client时,将直接在客户端缓存中读取数据。
总结
需注意事件的发生时间段
OnActionExecuting在action method调用前发生。
OnActionExecuted在action method调用后发生, 但是在result执行前发生 (在 view 呈现前)
OnResultExecuting在result执行前发生(在view 呈现前)
OnResultExecuted 在result执行后发生(在view 呈现后)
源代码下载: MVCActionFilters.rar
http://files.cnblogs.com/jasenkin/MVCActionFilters.rar