ASP.NET MVC和IEcaching – 操纵响应头无效
背景
我试图帮助一个同事debugging过去6个月没有问题的问题。 在最近部署ASP.NET MVC 2应用程序FileResult
,强制用户打开或保存PDF文件的FileResult
响应在客户端计算机上存在足够长的时间, FileResult
PDF阅读器无法打开它们。
IE的早期版本(特别是6)是唯一受影响的浏览器。 Firefox和Chrome以及更新版本的IE(> 8)都按预期行事。 考虑到这一点,下一节将定义重新创build问题所需的操作。
行为
- 用户单击一个指向一个操作方法的链接(带有
href
属性的普通超链接)。 - 操作方法生成一个表示为字节stream的PDF。 该方法总是重新创buildPDF。
-
在action方法中,头文件被设置为指示浏览器如何caching响应。 他们是:
response.AddHeader("Cache-Control", "public, must-revalidate, post-check=0, pre-check=0"); response.AddHeader("Pragma", "no-cache"); response.AddHeader("Expires", "0");
对于那些不熟悉标题的人来说:
一个。 caching控制:公共
指示响应可以被任何cachingcaching,即使它通常只在非共享caching中是不可caching或可caching的。
湾 caching控制:必须重新validation
当高速caching接收到的响应中存在must-revalidate指令时,该高速caching不应该在该条目失效之后才使用该条目来响应后续请求,而不必先用源服务器对其进行重新validation
C。 caching控制:预先检查 (与IE5一起引入)
定义以秒为单位的时间间隔,之后必须检查实体的新鲜度。 在用户显示资源之后可能会发生检查,但确保在下一次往返中,caching副本将是最新的。
d。 caching控制:后检查 (与IE5引入)
定义以秒为单位的时间间隔,在此之前必须检查实体的新鲜度,然后向用户显示资源。
即 Pragma:no-cache (确保向后兼容HTTP / 1.0)
当请求消息中存在no-cache指令时,应用程序应该将请求转发给原始服务器,即使它具有正被请求的caching副本
F。 过期
Expires实体标题字段给出了响应被认为是陈旧的date/时间。
-
我们从操作中返回文件
return File(file, "mime/type", fileName);
-
用户将看到一个打开/保存对话框
- 点击“保存”按预期工作,但点击“打开”启动PDF阅读器,但当读者试图打开文件时,存储的临时文件IE已经被删除,所以它抱怨文件丢失了是)。
这里有六个其他的应用程序使用相同的头文件强制在用户的Excel,CSV,PDF,Word,和大量的其他内容,从来没有一个问题。
问题
- 标题是否正确,我们正在尝试做什么? 我们希望文件临时存在(获取caching),但是总是被新版本取代,即使请求可能是相同的)。
响应头文件在返回FileResult
之前在action方法中设置。 我已经要求我的同事尝试创build一个从FileResult
inheritance的新类,而是重写ExecuteResult
方法,以便它修改头,然后执行base.ExecuteResult()
– 而不是状态。
我有一个预感“0”的“Expires”头是罪魁祸首。 根据这个W3C文章 ,将其设置为“0”意味着“已经过期”。 我希望它过期,我只是不希望IE应用程序处理之前将其从文件系统中删除它有机会打开它。
一如既往,谢谢!
编辑:解决scheme
在进一步的testing中(使用Fiddler来检查头文件),我们看到我们认为被设置的响应头并不是被浏览器解释的。 由于我自己并不熟悉这些代码,所以我并不知道一个潜在的问题:头部在动作方法之外被踩踏了。
不过,我打算把这个问题留下来。 还有一个突出的地方就是: Expires
头文件的值为0
和-1
之间似乎有一些差异。 如果有人可以通过devise来声明差异,关于IE ,我还是想听听。 至于一个解决scheme,上面的头文件在所有浏览器中的Expires
值设置为-1
按照预期工作。
更新1
该post如何在所有浏览器上控制网页caching? 详细介绍了在设置Expires = 0的帮助下,可以在所有浏览器中防止caching。我仍然没有在这个0
vs -1
参数上出售…
我想你应该使用
HttpContext.Current.Response.Cache.SetMaxAge (new TimeSpan (0));
要么
HttpContext.Current.Response.Headers.Set ("Cache-Control", "private, max-age=0");
设置max-age=0
这意味着没有什么更多的caching重新validation(见这里 )。 如果在头文件中额外设置了ETag
,并使用自定义的数据散列校验和,则先前请求中的ETag将被发送到服务器。 服务器既可以返回数据,也可以在数据与以前完全一样的情况下,返回空主体和HttpStatusCode.NotModified
作为状态码。 在这种情况下,Web浏览器将从本地浏览器caching中获取数据。
我推荐你使用Cache-Control: private
来强制两个重要的事情:1)closures代理上的数据caching,有时候会有非常积极的caching设置2)它允许caching数据,但不允许共享与另一个用户的caching。 它可以解决隐私问题,因为您返回给一个用户的数据可能不被其他用户读取。 顺便说一下,默认情况下,HTTP头中的代码HttpContext.Current.Response.Cache.SetMaxAge (new TimeSpan (0))
设置Cache-Control: private, max-age=0
。 如果你想使用Cache-Control: public
你可以使用SetCacheability (HttpCacheability.Public);
覆盖行为或使用Headers.Set
而不是Cache.SetMaxAge
。
如果您有兴趣研究HTTP协议的更多caching选项,我会build议您阅读caching教程 。
更新 :我决定写更多的信息来清除我的位置。 即使像Mozilla 2.7,Netscape 2.0和Internet Explorer 3.0这样的老的浏览器也支持Wikipedia 的信息 ,它支持1996年3月的RFC 2068中描述的HTTP / 1.1的预标准。所以我想(但不testing)旧的网页浏览器支持max-age=0
HTTP头。 Netscape 2.06和Internet Explorer 4.0以任何方式明确支持HTTP 1.1。
所以你应该首先问你:使用哪个HTML标准? 你仍然使用HTML 2.0而不是1997年1月发布的更晚期的HTML 3.2吗? 我想你至less使用了1997年12月发布的HTML 4.0。所以如果你至less在HTML 4.0中构build你的应用程序,你的网站可以面向支持HTTP 1.1的Web客户端,而忽略(不支持)Web客户端不支持HTTP 1.1。
现在关于其他“caching控制”标题为“私人,最大年龄= 0”。 包括标题在我看来是纯粹的偏执狂 。 由于我自己有一些caching问题,我还试图包含不同的其他标题,但稍后在仔细阅读RFC2616的14.9节之后,我只使用“Cache-Control:private,max-age = 0”。
唯一可以另外讨论的“caching控制”标题是我之前引用的14.9.4节中描述的“必须重新validation”。 这是报价:
必须重新validation指令对于支持某些协议function的可靠操作是必要的。 在任何情况下,HTTP / 1.1caching都必须遵守must-revalidate指令。 特别是如果caching由于某种原因无法到达原始服务器,它必须生成504(网关超时)响应。
当且仅当在实体上重新validation请求失败时,服务器才应该发送必须重新validation的指令,否则会导致不正确的操作,比如默默无闻的财务事务。 收件人不得采取任何违反此指令的自动操作,并且如果重新validation失败,则不得自动提供未经validation的实体副本。
虽然不build议这样做,但是在严格的连接约束条件下运行的用户代理可能会违反这个指令,但是,如果是的话,必须明确地警告用户已经提供了未经validation的响应。 警告务必提供在每个未经validation的访问,并应该需要明确的用户确认。
有时如果我有Internet连接的问题,我看到“网关超时”消息的空白页面。 它来自“必须重新validation”指令的使用。 我不认为“网关超时”消息真的帮助用户。
因此,如果在呼叫他的老板时听到“忙”信号,那么人们更喜欢开始自毁程序,还应该在“caching控制”报头中使用“必须重新validation”指令。 其他人,我build议只使用“caching控制:私人,最大年龄= 0”等等。
我知道这是迟到了,但是这个链接可以帮助大家对这个话题感兴趣: http : //dotnet.dzone.com/articles/output-caching-aspnet-mvc
对于IE,我记得必须设置Expires: -1
。 如何防止在Internet Explorer中的caching似乎用下面的代码片段确认这一点。
<% Response.CacheControl = "no-cache" %> <% Response.AddHeader "Pragma", "no-cache" %> <% Response.Expires = -1 %>
回顾代码,这是我发现的。 另外,我隐约记得,如果你设置Cache-Control: private
可能无法正确使用SSL。
Response.AddHeader("Cache-Control", "no-cache"); Response.AddHeader("Expires", "-1");
所以,你不想caching,嗯? 提到-1
,但使用Response.Cache
方法:
// Stop Caching in IE Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache); // Stop Caching in Firefox Response.Cache.SetNoStore();
但是, ASP页面caching问题(IE8)说这个代码不起作用。
- 在ASP.NET MVC中,我如何使用C#代码中的Razor @ Url.Content()助手?
- 在web.config转换中replaceIIS重写规则
- ASP.NET WebApi:如何使用WebApi HttpClient执行file upload的多部分文章
- 如何从相当复杂的Json响应中在C#Asp.Net中创build一个Dto
- 如何在ASP.NET MVC中进行分页?
- 如何从urlstring中删除端口号
- 属性声明中的“新”关键字在c#
- 如何在IIS7上运行时将maxAllowedContentLength设置为500MB?
- 无法打开login请求的数据库“testing”。 login失败。 用户'xyz \ ASPNET'login失败