创建一个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 { 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,同时允许读操作可以发生:

 

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<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时,将直接在客户端缓存中读取数据。

你可能感兴趣的:(attribute)