【问题标题】:ASP.NET MVC and IE caching - manipulating response headers ineffectiveASP.NET MVC 和 IE 缓存 - 操作响应标头无效
【发布时间】:2012-03-03 07:00:32
【问题描述】:

背景

我正在尝试帮助一位同事调试过去 6 个月内没有出现的问题。在最近部署 ASP.NET MVC 2 应用程序后,FileResult 强制用户打开或保存 PDF 文件的响应在客户端计算机上存在足够长的时间以供 PDF 阅读器打开它们时遇到问题。

IE 的早期版本(尤其是 6)是唯一受影响的浏览器。 Firefox 和 Chrome 以及更新版本的 IE (>8) 都按预期运行。考虑到这一点,下一部分定义了重新创建问题所需的操作。

行为

  1. 用户单击指向操作方法的链接(带有href 属性的普通超链接)。
  2. action 方法生成一个表示为字节流的 PDF。 always 方法重新创建 PDF。
  3. 在 action 方法中,设置了 headers 来指示浏览器如何缓存响应。它们是:

    response.AddHeader("Cache-Control", "public, must-revalidate, post-check=0, pre-check=0");
    response.AddHeader("Pragma", "no-cache");
    response.AddHeader("Expires", "0");
    

    对于那些不熟悉headers 所做的事情的人:

    一个。 缓存控制:公共

    表示响应可以被任何缓存缓存,即使它通常是不可缓存的或只能在非共享缓存中缓存。

    b. 缓存控制:必须重新验证

    当缓存接收到的响应中存在 must-revalidate 指令时,该缓存不能在条目变得陈旧后使用该条目来响应 后续请求,无需先使用源服务器重新验证它

    c。 Cache-Control: pre-check(IE5引入)

    以秒为单位定义一个时间间隔,在此之后必须检查实体的新鲜度。检查可能会在向用户显示资源后进行,但可确保在下一次往返中缓存的副本是最新的。

    d。 Cache-Control: post-check(在 IE5 中引入)

    以秒为单位定义一个时间间隔,在该时间间隔之后,必须在向用户显示资源之前检查实体的新鲜度。

    e。 Pragma: no-cache(确保向后兼容 HTTP/1.0)

    当请求消息中存在 no-cache 指令时,应用程序应该将请求转发给源服务器,即使它有正在请求的缓存副本

    f。 过期

    Expires 实体标头字段给出了响应被视为过时的日期/时间。

  4. 我们从动作中返回文件

    return File(file, "mime/type", fileName);
    
  5. 向用户显示打开/保存对话框

  6. 单击“保存”按预期工作,但单击“打开”会启动 PDF 阅读器,但 IE 存储的临时文件在阅读器尝试打开文件时已被删除,因此它抱怨文件丢失(确实如此)。

这里有六个其他应用程序使用相同的标题强制 Excel、CSV、PDF、Word 和大量其他内容向用户展示,从来没有出现过问题。

问题

  • 标题是否适合我们正在尝试执行的操作?我们希望文件暂时存在(被缓存),但总是被新版本替换,即使请求可能相同)。

在返回 FileResult 之前,在 action 方法中设置了响应标头。我已经要求我的同事尝试创建一个继承自 FileResult 的新类,并改为覆盖 ExecuteResult 方法,以便它修改标头,然后改为使用 base.ExecuteResult() - 对此没有任何状态。

我有一种预感,“0”的“Expires”标题是罪魁祸首。根据this W3C article,将其设置为“0”意味着“已经过期”。我确实希望它过期,我只是不希望 IE 在处理它的应用程序有机会打开它之前将其从文件系统中删除。

一如既往,谢谢!

编辑:解决方案

经过进一步测试(使用 Fiddler 检查标头),我们发现我们认为设置的响应标头不是浏览器正在解释的标头。由于我自己不熟悉代码,我不知道一个潜在的问题:标题在操作方法之外被踩到了。

不过,我将保留这个问题。仍然很突出的是:Expires 标头的值 0-1 之间似乎存在一些差异。如果有人可以声称在设计上与 IE 存在差异,我仍然想听听。至于解决方案,在所有浏览器中将Expires 值设置为-1 时,上述标头确实可以正常工作。

更新 1

How to control web page caching, across all browsers? 的帖子详细描述了通过设置 Expires = 0 可以在所有浏览器中防止缓存。我仍然不赞成 0-1 的论点...

【问题讨论】:

  • 您可能想从这里使用NoCacheAttributestackoverflow.com/questions/1160105/…
  • @lasseespeholt:我确实希望浏览器缓存文件;我只是希望它立即过期,所以它总是被替换。根本不缓存它会是个问题。

标签: asp.net asp.net-mvc-2 iis caching http-headers


【解决方案1】:

我认为你应该使用

HttpContext.Current.Response.Cache.SetMaxAge (new TimeSpan (0));

HttpContext.Current.Response.Headers.Set ("Cache-Control", "private, max-age=0");

设置max-age=0,这意味着缓存重新验证(参见here)。如果您将在标头中额外设置ETag,并使用您自定义的数据哈希校验和,则来自先前请求的ETag 将被发送到服务器。服务器可以返回数据,或者如果数据与以前完全相同,它可以返回空正文和HttpStatusCode.NotModified作为状态码。在这种情况下,网络浏览器将从本地浏览器缓存中获取数据。

我建议您使用Cache-Control: private,它强制执行两件重要的事情:1) 关闭代理上的数据缓存,它有时具有非常激进的缓存设置 2) 它允许缓存数据,但不允许与其他用户共享缓存。它可以解决隐私问题,因为您返回给一个用户的数据可能不允许其他用户读取。顺便说一句,代码HttpContext.Current.Response.Cache.SetMaxAge (new TimeSpan (0))默认在HTTP头中设置Cache-Control: private, max-age=0。如果您确实想使用Cache-Control: public,您可以使用SetCacheability (HttpCacheability.Public); 覆盖行为或使用Headers.Set 而不是Cache.SetMaxAge

如果您有兴趣研究更多 HTTP 协议的缓存选项,我建议您阅读caching tutorial

更新:我决定再写一些信息来澄清我的立场。对应于来自维基百科的the information,即使像 Mosaic 2.7、Netscape 2.0 和 Internet Explorer 3.0 这样的旧网络浏览器支持 1996 年 3 月,RFC 2068 中描述的 HTTP/1.1 的预标准。所以我想(但没有测试它)旧 Web 浏览器支持 max-age=0 HTTP 标头。无论如何,Netscape 2.06 和 Internet Explorer 4.0 都明确支持 HTTP 1.1。

所以您应该首先问您:您使用哪些 HTML 标准?您是否仍然使用 HTML 2.0 而不是 1997 年 1 月发布的更晚的 HTML 3.2?我想您至少使用 1997 年 12 月发布的 HTML 4.0。因此,如果您至少在 HTML 4.0 中构建应用程序,您的站点可以面向支持 HTTP 1.1 的 Web 客户端,而忽略(不支持)支持 HTTP 1.1 的 Web 客户端不支持 HTTP 1.1。

现在将其他“Cache-Control”标头称为“private, max-age=0”。在我看来,包括标题是纯粹的偏执狂。由于我自己有一些缓存问题,我也尝试包含不同的其他标头,但后来在仔细阅读了 RFC2616 的第 14.9 节后,我只使用了“Cache-Control: private, max-age=0”。

唯一可以额外讨论的“Cache-Control”标头是我之前引用的第 14.9.4 节中描述的“must-revalidate”。这是报价:

must-revalidate 指令对于支持可靠 某些协议功能的操作。在任何情况下 HTTP/1.1 缓存必须遵守 must-revalidate 指令;特别是, 如果缓存由于任何原因无法到达源服务器,它必须 生成 504(网关超时)响应。

服务器应该发送 must-revalidate 指令当且仅当 未能重新验证对实体的请求可能会导致 不正确的操作,例如默默未执行的财务 交易。收件人不得采取任何自动操作 违反此指令,并且不得自动提供 如果重新验证失败,则实体的未验证副本。

虽然这是 不推荐,用户代理在严重连接下运行 约束可能违反此指令,但如果是,必须明确 警告用户提供了未经验证的响应。这 必须在每个未经验证的访问上提供警告,并且应该 需要明确的用户确认。

有时,如果我遇到 Internet 连接问题,我会看到带有“网关超时”消息的空白页面。它来自“必须重新验证”指令的使用。我不认为“网关超时”消息真的对用户有帮助。

因此,如果在呼叫老板时听到“忙”信号,人们更愿意开始自毁程序,应该在“Cache-Control”标头中另外使用“must-revalidate”指令。我建议其他人只使用“Cache-Control: private, max-age=0”,仅此而已。

【讨论】:

  • @Cory:我附加我的答案以更明确地明确为什么我建议只使用“Cache-Control: private, max-age=0”without 任何其他选项以及为什么我认为您应该忽略不支持“max-age=0”标头的 Web 客户端。
  • 感谢您提供详细信息。我们会给private, max-age=0 一个机会,让你知道它是怎么回事。
  • @Cory:你的测试有结果吗?
  • 还没有。这个项目不是我的,所以我在等我的同事回复我。但是,我将为您提供怀疑的好处,并为您提供如此详细的回复而奖励您。如果有任何变化或另有说明,我会通知您。谢谢!
  • @Cory:好的!不客气!无论如何,我很想知道Cache-Control: private, max-age=0 的使用是否解决了您的缓存问题。
【解决方案2】:

对于 IE,我记得必须设置 Expires: -1How to prevent caching in Internet Explorer 似乎用以下代码 sn-p 确认了这一点。

<% 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");

另外,So, You Don't Want To Cache, Huh? 提到了-1,但使用了Response.Cache 上的方法:

// Stop Caching in IE
Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
// Stop Caching in Firefox
Response.Cache.SetNoStore();

但是,ASP Page caching issue (IE8) 说此代码不起作用。

【讨论】:

  • Expires = 0Expires = -1 (应该)相同; W3C 规范将 0 定义为“已经过期”,相当于 -1 的含义。除了知识库文章之外,您还有其他资源来支持它吗?此外,使用 -1 而不是 0 进行测试会产生相同的行为。
  • 没关系,你可能是对的;但是,我们无意中在另一个地方设置了标题,这两者的组合导致实际解释的标题与我们要求的不同。那么,为了获得赏金,你知道为什么 IE6 违反了Expires 规范的规则吗?
  • 我记得-1 是 IE AJAX 请求的问题,但没有权威来源 - 这是一个链接:dashbay.com/2011/05/internet-explorer-caches-ajax
猜你喜欢
  • 1970-01-01
  • 2017-12-29
  • 1970-01-01
  • 2020-09-02
  • 1970-01-01
  • 2010-12-06
  • 1970-01-01
  • 1970-01-01
  • 2011-05-03
相关资源
最近更新 更多