细说 ASP.NET控制HTTP缓存[转]

阅读目录

在上篇博客【细说 ASP.NET Cache 及其高级用法】中, 我给大家介绍了ASP.NET Cache,这种服务端使用的缓存API 。在我们开发一个ASP.NET网站的过程中,其实有很多地方都是可以使用缓存的, 只是由于ASP.NET是一种基于服务端的开发平台,自然我们也经常在服务端的代码中使用各类缓存技术, 然而,由于WEB应用程序的服务对象是客户端的浏览器,通常来说,我们并不能直接控制浏览器的行为,但是, 浏览器却可以根据后台网站的指示,采取一些优化的方式来更快地呈现页面。 客户端浏览器也有自己的缓存机制,通常浏览器也使用缓存来优化一些页面的显示过程, 不过,我们并不能直接使用C#代码控制浏览器的缓存操作,但我们可以告诉浏览器如何使用缓存,从而达到优化网站性能的目的。

这次博客的主题是:用ASP.NET控制HTTP请求过程中浏览器缓存的一些方法。

回到顶部

正常的HTTP请求过程

在开始介绍浏览器在HTTP请求过程前,我想有必要先来看一下浏览器请求一个普通ASPX页面的过程。
说明:本文在介绍HTTP请求过程时,会大量使用Fiddler来分析具体的请求过程。

上图是一个普通的ASPX页面的请求过程,说它普通是因为:我在创建这个页面后,没对它做任何缓存方面的处理。
图片中我们可以可以看到服务器的响应状态为:HTTP/1.1 200 OK,这是一个服务器成功响应的标志。
另外,要注意图片中的Cache响应头部分,我之所以就红线框出来,是想提醒您注意这块的内容将在后面的小节中发生改变,
到时候请注意对比它们。而这里所反映的情况其实也只是默认值而已,它并不表示此页面需要缓存。

回到顶部

缓存页的请求过程

下面再来看一个缓存页面的请求过程:

对比上一张图片中可以看出,现在多了【max-age】,【Expires】以及【Last-Modified】这三个响应头。

这三个头的含义如下:
1. max-age,Expires:要表达的意思基本差不多。max-age表示某次HTTP的响应结果应该缓存多少秒。
   而Expires是说某次HTTP的响应结果应缓存到什么时候过期,此时间是一个UTC时间。
   另一个Date头表示HTPP响应的发出时间,我们可以发现 Date + max-age = Expires
2. Last-Modified:服务端告诉客户端本次响应返回的HTTP文档的最后修改时间。这个头与304的实现有关,后面再来解释。

分析了HTTP请求过程后,我们再来看一下服务端的页面是什么样子的:

注意:上面代码中最关键的一行代码为:

<%@ OutputCache Duration="10" VaryByParam="None" %>

正是由于使用了这个OutputCache指令,最后才会输出上面那几个响应头,用来告诉浏览器此页面需要缓存10秒钟。

说到这里,可能有些人想有疑惑了:缓存页在什么时候会起到什么作用呢?
为了演示缓存页所带来的现实意义,我将点击页面的这些链接并以截图的形式来说明:在一系列请求过程中页面的显示情况,
并以页面的显示结果来分析缓存所起的作用。

先来看看这个页面的显示截图:

页面很简单,主要是显示了页面的生成时间与一个刷新链接。
从上面提供的页面代码,我们应该能知道这个页面如果是由服务端生成的,则会显示当前的时间。

不过呢,当我一直(频繁)点击【刷新本页】那个链接时,页面的时间并没有发生改变,当我发现时间改变时,页面已显示成这个样子了:

由于测试过程中,我一直打开了Fiddler,正好我也把Fiddler监视到的请求结果截图下来了:

从Fiddler中,我看到FireFox其实只发生了二次请求,但我点击那个【刷新本页】起码超过10次。

以上的这一切,只说明一个事实:如果页面需要跳转到某个缓存页时,且那个缓存页还没过期,那么浏览器并不会发起到服务器的请求,而是使用缓存页。

小结:页面缓存所带来的好处是:缓存页面在过期前,用户通过点击跳转链接所引发的后续访问,并不会再次请求服务器。
这对服务器来说可以减少许多访问次数,因此使用这个特性可以很好地改善程序性能。

回到顶部

缓存页的服务端编程

前面演示了使用OutputCache指令所产生的缓存页的效果,由于那些指令需要在页面的设计阶段就写到页面上,因此显得不够灵活,
不能在运行时调整,虽然ASP.NET也允许我们使用CacheProfile来引入定义在Web.config中的配置,但配置还是没有运行时的代码灵活。
我们再来看看如何用代码来实现上面的效果。

其实用代码实现缓存页也很简单,只需要这样就可以了:

protected void Page_Load(object sender, EventArgs e)
{
    Response.Cache.SetCacheability(HttpCacheability.Public);
    Response.Cache.SetExpires(DateTime.Now.AddSeconds(10.0));
}

其实关键也就是对Response.Cache的调用。
注意:Response.Cache与我上篇【细说 ASP.NET Cache 及其高级用法】博客所讲的Cache不是一回事,二者完全不相干。
Response.Cache提供:用于设置缓存特定的 HTTP 标头的方法和用于控制 ASP.NET 页输出缓存的方法。

我们还是来说前面的二段示例代码。可能有些人会想,它们最终的结果真的会是一致的吗?

要想回答这个问题,我想有必要看一下前面用OutputCache指令的那个页面最终运行的代码是个什么样子的。

在ASP.NET的临时编译目录中,我找到了前面那个文件的一个由ASP.NET处理后的版本:

我们可以看到页面针对OutputCache指令的设置,最终会调用Page类定义一个方法中:

protected internal virtual void InitOutputCache(OutputCacheParameters cacheSettings)

那个方法实在太长,最终的处理方式也还是在调用this.Response.Cache,有兴趣的可以自己去看看那个方法。 至于这个方法的参数为什么是OutputCacheParameters,我想这个容易理解:方便将OutputCache指令的参数全部一起传入嘛。

所以,也正因为这个缘故,我们也可以直接在代码中调用Response.Cache的一些方法来实现相同的效果, 由于代码可以在运行时根据各种参数调整缓存策略,因此会更加灵活,而且可以采用基类的继承方式来简化实现。

注意:如果使用OutputCache指令再配合OutputCache Module的使用,可以实现304的效果。

回到顶部

什么是304应答?

通过前面的示例,我们已经看到缓存带来的好处:那就是可以减少到服务器的访问,由于不访问服务器就能显示页面,这对于服务器来说, 能减轻一定的访问压力。但是,如果用户强制刷新浏览器,那么浏览器将会忽略缓存页,直接向服务器重新发起请求。

也就是说:缓存页在用户强制刷新浏览器时会无效。
但是,我们之所以使用缓存页,是因为我们希望告诉浏览器:这些数据在一定时间内,并不会发生变化,因此根本不需要再次请求服务器了。
然而,我们不能阻止用户的行为。由于浏览器的重新访问,我们原来设想的缓存想法将会落空,最后的结果是:
页面在服务器中重新执行,产生的HTML代码将重新发送到客户端。而这一重新刷新的结果可能也是无意义的,因为数据可能根本没有发生变化,
因此得到的页面也是不可能有变化的。

再来举个简单的例子来说吧:客户端要浏览一张图片。
当浏览器第一次要访问图片时,浏览器肯定是没有它的任何缓存记录的,此时它去访问服务器,服务器也返回图片的内容了。
但由于图片可能会被多个页面所引用,而它被修改的可能性是很小的。
因此没有必要为同一浏览器的多次请求都去读取图片并返回图片的内容,这样做既影响性能也学浪费带宽。
于是,像IIS这样服务器软件针对这类静态文件的访问时,都会在响应头上输出一些标记,用来告之浏览器这个文件你可以缓存起来了。

还是回到前面所说的【用户强制刷新】问题,此时的IIS又会如何处理呢?请看下图:

注意哦,此时除了HTTP状态码变成304之外,没有任何数据返回哦。

为了让您对304应答有个深刻的印象,我截了一张状态码为200的图片响应结果:

通过这二张图片的对比,现在看清楚了吧:304和200并不只是数字上的差别,最重要的差别在于有没有返回结果。

没有返回结果,浏览器该如何显示?
您会有这样的疑虑吗?
其实不用担心,此时浏览器会使用它缓存版本来显示。也就是说:不管用户如何强制刷,服务器就是不返回结果,但仍然可以正常显示。

显然,这个效果就是我们想要的。
前面所说的缓存页遭用户强刷的问题,如果采用这种方法,就比较完美了。
不过,有一点我要提醒您:Visual Studio自带的那个WebDev.WebServer.exe不支持304应答,所以您就不要拿它试验了,不会有结果的。

回到顶部

如何编程实现304应答

前面我们看到了304应答的效果。不过,在ASP.NET中,我们开发的程序,是动态页面,而不是图片,
我们更希望某个页面能以这种方式缓存一段时间,我想这个需求或许会更有意义。

下面,我就来演示如何通过编程的方式实现它。
接下来的示例中,页面的显示还是那个样,显示页面在服务器上产生的时间,时间变化了,说明页面被重新执行了。

重新截一系列的图片,我认为意义也不大,我就截一张图片展现多次强刷而产生的过程

上图反映了我多次请求某个ASPX页面的过程,从图片中可以看出,只有第一次是200的响应,后面全是304,是您所期待的结果吧。

再来看看它的实现代码吧:

虽然代码并不复杂,但我还是打算来解释一下:
在浏览器第一次请求页面时,会执行SetLastModified的调用,它会在响应时输出一个"Last-Modified"这个响应头,
然后,当浏览器再次访问这个页面时,会将上次请求所获取的"Last-Modified"头的内容
以"If-Modified-Since"这个请求头的形式发给服务端,此时服务器就可以根据具体逻辑来判断要不要使用304应答了。

在前面的请求图片的示例中,服务器以图片文件的最后修改时间做为"Last-Modified"发给浏览器,
浏览器在后续请求那张图片时,又以"If-Modified-Since"的形式告之服务端,此时服务端只要再次检查一下这张图片就知道图片在上次访问后有没有发生修改,
如果没有修改,当然就以304的形式告之浏览器:继续使用缓存版本。

还是前面的请求图片的示例,其实服务端还使用了另一对【请求/响应】头:

这二个头的使用方式是:服务端输出一个ETag头,浏览器在接收后,以If-None-Match的形式在后续请求中发送到服务端,
供服务端判断是否使用304应答。

"Last-Modified"与"ETag"这二者,事实上只需要使用一个就够了,关键还是看服务端如何处理它们,浏览器只是在接收后,下次再发出去而已。

不过,前面的示例代码并没有使用缓存头,事实上,也可以带上它,这样可以尽量减少对服务器的访问,毕竟用户不会一直强刷浏览器。
这二种方式虽然有较大差别,但它们绝对是可以互补的。

为了能形象的描绘缓存页(或者其它文档)的请求过程,我画了张示意图供大家参考:

回到顶部

如何避开HTTP缓存

前面小节中,介绍了二种方法使用浏览器的缓存。但有些时候可能反而希望浏览器能放弃它缓存的结果。
现在的浏览器都有缓存功能,尤其是对一些静态文件,比如:图片,JS,CSS, HTML文件,都能缓存。
但有时候我们需要更新CSS, JS文件呢,浏览器如果还使用它的缓存版本,显然就有问题了。
而且有些网站使用了URL重写,使原来的动态页面扩展名也变成静态的HTML文件了,
因此,仍然希望浏览器在某些时候能够不要缓存这些伪静态页面。

此时,我们就希望浏览器放弃从HTTP请求所获得的结果了。
一般说来,浏览器在处理(它认为的)静态文件时,会按照URL为kEY来保存那些缓存结果,
因此,通常的解决办法也就是修改URL,比如:原来是请求abc.js的,要改成abc.js?t=23434,后面要跟上一个参数,
让以前的缓存不起作用。至于参数t的取值可以根据文件的最后修改时间,也可以手工指定,总之只要改变它就可以了。

但是,对于伪静态的页面,我们不能再使用这种方法了,原因就不用解释了吧。
那么,可以采用在服务端输出一个响应头,通过响应头的方式告之浏览器,不要缓存此文件。
比如,可以调用这个方法:

Response.Cache.SetNoStore();

它会生成这样的响应头内容:

Cache-Control: private, no-store

许多浏览器都能识别它。还有另一种方法是设置一个已过期的过期时间。

前面所说的在URL中加额外参数的做法,在JS中也比较常用,比如 JQuery就支持让某个Ajax请求不缓存, 它的方式就是设置{cache: false},最终它便会在生成的URL中加上一个临时参数,以保证后面的请求的地址是不重复的, 最终达到避开缓存的目的。JQuery的使用太简单,我就不再给出示例代码了。

http://www.cnblogs.com/fish-li/archive/2012/01/11/2320027.html

时间: 2024-10-11 11:06:52

细说 ASP.NET控制HTTP缓存[转]的相关文章

asp.net页面清除缓存2(转)

ASP.NET 提供三种主要形式的缓存:页面级输出缓存.用户控件级输出缓存(或称为片段缓存)和缓存 API. 输出缓存和片段缓存的优点是非常易于实现,在大多数情况下,使用这两种缓存就足够了.而缓存 API 则提供了额外的灵活性(实际上是相当大的灵活性),可用于在应用程序的每一层利用缓存. Steve 的缓存提示 尽早缓存:经常缓存 您应该在应用程序的每一层都实现缓存.向数据层.业务逻辑层.UI 或输出层添加缓存支持.内存现在非常便宜 — 因此,通过以智能的方式在整个应用程序中实现缓存,可以获得很

asp.net页面清除缓存1

1.页面每次打开页面的时候都要清除本页面的缓存 页面打开时候,由于缓存的存在,刚刚更新的数据有时无法在页面得到刷新,当这个页面作为模式窗口打开问题更加明显 2.asp.net页面缓存的清除 ASP.NET清除页面缓存 (1) Response.Buffer = true;Response.ExpiresAbsolute = System.DateTime.Now.AddSeconds(-1);Response.Expires = 0;Response.CacheControl = "no-cac

ASP.Net控制不同的人编辑word文档中不同的可编辑区域

ASP.Net控制不同的人编辑word文档中不同的可编辑区域的完整示例 2010-10-15 11:43238人阅读评论(0)收藏举报 网页来源:http://blog.csdn.net/coco99/article/details/5942895 本文演示了如何使用C#在ASP.NET里调用Word限制用户只能编辑word文档中自己有权编辑的区域. 1.项目目的 演示使用不同的用户登录系统,打开同一个文件(不必同时打开),可以编辑的区域不一样,每个人都有属于自己的编辑区域. 2.解决思路 利用

029.ASP.Net中的缓存机制

ASP.Net中的缓存 输出缓存1. 整页缓存 缓存整个页面的输出结果 Duration 缓存时间:绝对过期 VaryByParam:依据参数值缓存,没有为None,多个用;分割 Location:缓存的位置 代码在 ftp 的 Cache目录内 2. 片段缓存 使用用户控件,将需要缓存的内容放入用户控件 指定shared=true,可以多个页面共享缓存结果 3.Substitution控件 在整页都缓存的情况下,可以部分更新内容 配置MethodName属性,对应的.cs中的方法原型是 str

servletResponse 控制浏览器缓存

//当访问一些资源文件时,我们希望,访问一次后,资源文件能够在缓存在浏览器中,当我们再次访问该资源时 //直接从缓存中去取,这样可以减少服务器的压力 package response; import java.io.IOException;import javax.servlet.ServletException;import javax.servlet.http.HttpServlet;import javax.servlet.http.HttpServletRequest;import ja

细说Asp.Net Web API消息处理管道(二)

在细说Asp.Net Web API消息处理管道这篇文章中,通过翻看源码和实例验证的方式,我们知道了Asp.Net Web API消息处理管道的组成类型以及Asp.Net Web API是如何创建消息处理管道的.本文在上篇的基础上进行一个补充,谈谈在WebHost寄宿方式和SelfHost寄宿方式下,请求是如何进入到Asp.Net Web API的消息处理管道的. WebHost寄宿方式: 在剖析Asp.Net WebAPI路由系统一文中,我们知道Asp.Net Web API在WebHost寄

HTTP协议控制浏览器缓存HTTP协议控制浏览器缓存

HTTP协议控制浏览器缓存 2015.01.25 SilenceHurts 当我们浏览网站的图片时,按下F12监控,很容易就发现,当我们第一次浏览该图片时,浏览器返回的代码是200,即请求正常,此时,不懂任何东西,我们按下F5刷新页面,注意,我们会发现监控下的代码返回不再是200,而是现实304 Not Modifid,即未修改. 原因:第一次我们访问图片时,是正常的,而第二次访问时现实304,因为此时我们浏览器发现页面未发生更改,此时使用的是本地的缓存,而并非服务器返回的代码,加快了速度. 设

html asp php java 清除缓存

HTML页面 <META HTTP-EQUIV="pragma" CONTENT="no-cache"><META HTTP-EQUIV="Cache-Control" CONTENT="no-cache, must-revalidate"><META HTTP-EQUIV="expires" CONTENT="Wed, 26 Feb 1997 08:21:57 GM

用ASP编程控制在IIS建立Web站点

''******************************************************* '' 创建一个WebServer '' 必须参数:WRoot,为创建站点的物理目录:WComment为站点说明:WPort为站点端口:ServerRun为是否自动运行 '' 当创建成功时返回1,失败时提示退出并返回0,当创建站点成功但启动失败时返回2 ''******************************************************* ''******