[ASP.NET MVC2 系列] Action Filters以及自定义OutputCache ActionFilterAttribute事件发生次序

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 属性使用的设置不建议通过设置“*”的值来使用所有参数进行区分。这可能会导致缓存溢出。

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框架支持多种不同类型的过滤器:

  1. Authorization filters – 实现IAuthorizationFilter 特性.
  2. Action filters – 实现IActionFilter 特性.
  3. Result filters – 实现IResultFilter 特性.
  4. Exception filters –实现IExceptionFilter 特性.

Filters 按照上面列出的顺序执行。例如, authorization filters 总是在action filters之前执行,exception filters在所有其他类型的filter之后执行.

ActionFilterAttribute 基类

为了使你能够更加容易的实现自定义的action filter, ASP.NET MVC框架包含一个ActionFilterAttribute 基类. 这个类实现了IActionFilterIResultFilter 接口,并且继承了Filter 类。

ActionFilterAttribute 基类拥有以下可以重载的方法:

  • OnActionExecuting在action method调用前发生。
  • OnActionExecuted在action method调用后发生, 但是在result执行前发生 (在 view 呈现前)
  • OnResultExecuting在result执行前发生(在view 呈现前)
  • OnResultExecuted 在result执行后发生(在view 呈现后)

 

创建一个ASP.NET MVC OutputCache ActionFilterAttribute

 

使用ASP.NET MVC 框架, 简单的指定OutputCache 指令并不能达到理想的效果. 幸好, ActionFilterAttribute让你能够在 controller action执行的前后运行代码.

让我们使用类似的方法来创建OutputCache ActionFilterAttribute

[OutputCache(Duration = 60, VaryByParam = "*", CachePolicy = CachePolicy.Server)]
public ActionResult Index()
{
    
// ...


 

我们将使用命名为CachePolicy的枚举类型来指定OutputCache 特性应怎样以及在哪里进行缓存:

public enum CachePolicy
{
    NoCache 
= 0,
    Client 
= 1,
    Server 
= 2,
    ClientAndServer 
= 3


 

1.实现client-side缓存

事实上,这是很容易的。在view呈现前,我们将增加一些HTTP头到响应流。网页浏览器将获得这些头部,并且通过使用正确的缓存设置来回应请求。如果我们设置duration为60,浏览器将首页缓存一分钟。

using System.Web.Mvc;

namespace MVCActionFilters.Web.Models
{
    
public class OutputCache:System.Web.Mvc.ActionFilterAttribute 
    {
        
public int Duration { getset; }
        
public CachePolicy CachePolicy { getset; }

        
public override void OnActionExecuted(ActionExecutedContext filterContext)
        {
            
if (CachePolicy == CachePolicy.Client || CachePolicy == CachePolicy.ClientAndServer)
            {
                
if (Duration <= 0return;

                
//用于设置特定于缓存的 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,同时允许读操作可以发生:

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; 

 

 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响应能够被读取。在视图呈现之后,将数据存储在高速缓存中:

 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的键。

  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<stringobject> 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

     
  • posted @ 2010-09-11 17:49  jasen.kin  阅读(6977)  评论(5编辑  收藏  举报