使用ASP.NET MVC 框架, 简单的指定OutputCache 指令并不能达到理想的效果. 幸好, ActionFilterAttribute让你能够在 controller action执行的前后运行代码.
让我们使用类似的方法来创建OutputCache ActionFilterAttribute。
[OutputCache(Duration = , VaryByParam = "*", CachePolicy = CachePolicy.Server)]
public ActionResult Index()
{
// ...
}
我们将使用命名为CachePolicy的枚举类型来指定OutputCache 特性应怎样以及在哪里进行缓存:
public enum CachePolicy
{
NoCache = ,
Client = ,
Server = ,
ClientAndServer =
}
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 <= ) 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 = , VaryByParam = "*",CachePolicy=Common.CachePolicy.Client)]
public string Cache()
{
return DateTime.Now.ToString();
}
设置CachePolicy为Common.CachePolicy.Client时,将直接在客户端缓存中读取数据。