在每一个web应用程序中, 有的情况下,你想在一段时间内缓存一个具体的页面HTML输出,因为相关的数据和处理并不是总是变化。这种缓存的响应是储存在服务器的内存中。因为没有必要的额外处理,它提供了非常快速的响应。使用经典的ASP.NET,你可以在.aspx页面上使用OutputCache指令,它告诉ASP.NET运行时在某一特定的时间段内来缓存响应数据。缓存可随参数而改变,这将导致产生依赖于参数的不同缓存响应。作为一个额外的功能,还可以发送一些HTTP头到客户端。在一段时间以内,客户端从浏览器缓存中加载页面。大的优势是你的web服务器将接收更少的客户端要求,因为他们仅仅是使用他们自己的缓存。

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

创建一个ASP.NET MVC OutputCache ActionFilterAttribute的更多相关文章

  1. 在一个空ASP.NET Web项目上创建一个ASP.NET Web API 2.0应用

    由于ASP.NET Web API具有与ASP.NET MVC类似的编程方式,再加上目前市面上专门介绍ASP.NET Web API 的书籍少之又少(我们看到的相关内容往往是某本介绍ASP.NET M ...

  2. ASP.NET没有魔法——开篇-用VS创建一个ASP.NET Web程序

    为什么写这一系列文章? 本系列文章基于ASP.NET MVC,在ASP.NET Core已经发布2.0版本,微服务漫天的今天为什么还写ASP.NET?. 答:虽然现在已经有ASP.NET Core并且 ...

  3. 2.第一个ASP.NET MVC 5.0应用程序

    大家好,上一篇对ASP.NET MVC 有了一个基本的认识之后,这一篇,我们来看下怎么从头到尾创建一个ASP.NET MVC 应用程序吧.[PS:返回上一篇文章:1.开始学习ASP.NET MVC] ...

  4. ASP.NET开发实战——(一)开篇-用VS创建一个ASP.NET Web程序

        本文是本系列文章第一篇,主要通过建立一个默认ASP.NET MVC项目来引出与ASP.NET MVC相关的功能,由于ASP.NET MVC一个简单的模板就具备了数据库操作.身份验证.输入数据校 ...

  5. 学习ASP.NET MVC(七)——我的第一个ASP.NET MVC 查询页面

    在本篇文章中,我将添加一个新的查询页面(SearchIndex),可以按书籍的种类或名称来进行查询.这个新页面的网址是http://localhost:36878/Book/ SearchIndex. ...

  6. 学习ASP.NET MVC(三)——我的第一个ASP.NET MVC 视图

    今天我将对前一篇文章中的示例进行修改,前一篇文章中并没有用到视图,这次将用到视图.对于前一个示例中的HelloWorldController类进行修改,使用视图模板文件生成HTML响应给浏览器. 一. ...

  7. 学习ASP.NET MVC(一)——我的第一个ASP.NET MVC应用程序

    学习ASP.NET MVC系列: 学习ASP.NET MVC(一)——我的第一个ASP.NET MVC应用程序 学习ASP.NET MVC(二)——我的第一个ASP.NET MVC 控制器 学习ASP ...

  8. NHibernate构建一个ASP.NET MVC应用程序

    NHibernate构建一个ASP.NET MVC应用程序 什么是Nhibernate? NHibernate是一个面向.NET环境的对象/关系数据库映射工具.对象/关系数据库映射(object/re ...

  9. 跟我学ASP.NET MVC之二:第一个ASP.NET MVC程序

    摘要: 本篇文章带你一步一步创建一个简单的ASP.NET MVC程序.  创建新ASP.NET MVC工程 点击“OK”按钮后,打开下面的窗口: 这里选择“Empty”模板以及“MVC”选项.这次不创 ...

随机推荐

  1. Java,PostgreSQL时间范围查询

    遇到一坑:对于如下代码 select * from order_mileagefuel where date > '2015-11-1' and date< '2015-11-5' 在Po ...

  2. hdu 1003 Max Sum(动态规划)

    解题思路: 本题在给定的集合中找到最大的子集合[子集合:集合的元素的总和,是所有子集合中的最大解.] 结果输出: 最大的子集合的所有元素的和,子集合在集合中的范围区间. 依次对元素相加,存到一个 su ...

  3. (翻译)初学者的object-C指南

    初学者的object-C指南 英文原文:http://blog.teamtreehouse.com/the-beginners-guide-to-objective-c-language-and-va ...

  4. pthread多线程编程的学习小结

    pthread多线程编程的学习小结  pthread 同步3种方法: 1 mutex 2 条件变量 3 读写锁:支持多个线程同时读,或者一个线程写     程序员必上的开发者服务平台 —— DevSt ...

  5. C++_Eigen函数库用法笔记——The Array class and Coefficient-wise operations

    The advantages of Array Addition and subtraction Array multiplication abs() & sqrt() Converting ...

  6. virtualbox 不能为虚拟电脑打开一个新任务/VT-x features locked or unavailable in MSR.

    确保了主机的BIOS中开启了Intel Virtual Technology,虚拟机配置中勾选了“启用VT-x/AMD-V”. 这是因为CPU不支持VT-X技术或者VT-X技术被锁定. 如果不打开虚拟 ...

  7. derby的三大缺陷

    derby的三大缺陷 derby数据库的嵌入式特性让人很流口水.但是,我刚打算将其用进我的项目中,却发现它没有好的分页查询方式,每次都返回所有符合条件的记录.oracle有rownum,mysql有l ...

  8. linux利用grep查看打印匹配的下几行或前后几行的命令

    转自:http://www.itokit.com/2013/0308/74883.html linux系统中,利用grep打印匹配的上下几行   如果在只是想匹配模式的上下几行,grep可以实现.   ...

  9. Ubuntu 下忘记mysql 密码

    在Ubuntu下忘记密码.可按以下步骤重设:1.停止mysql服务: #/etc/init.d/mysql stop;2,安全模式启动mysql,略过密码验证: # sudo mysqld_safe ...

  10. 关于thinkphp 开发的网站部署问题

    公司一个网站用thinkphp 开发的,由wamp环境移植到lamp环境 出现错误.提示无法生成缓存文件. 原因是thinkphp 的一些目录需要重新生成,所以将一个新的thinkphp 核心包应用后 ...