ASP.NET MVC 和 IE 缓存 - 操作响应头无效 [英] ASP.NET MVC and IE caching - manipulating response headers ineffective

查看:22
本文介绍了ASP.NET MVC 和 IE 缓存 - 操作响应头无效的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm attempting to help a colleague debug an issue that hasn't been an issue for the past 6 months. After the most recent deployment of an ASP.NET MVC 2 application, FileResult responses that force a PDF file at the user for opening or saving are having trouble existing long enough on the client machine for the PDF reader to open them.

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

Earlier versions of IE (expecially 6) are the only browsers affected. Firefox and Chrome and newer versions of IE (>8) all behave as expected. With that in mind, the next section defines the actions necessary to recreate the issue.

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

  1. User clicks a link that points to an action method (a plain hyperlink with an href attribute).
  2. The action method generates a PDF represented as a byte stream. The method always recreates the PDF.
  3. In the action method, headers are set to instruct browsers how to cache the response. They are:

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

对于那些不熟悉 标头 做:

For those unfamiliar with exactly what the headers do:

一个.缓存控制:公开

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

Indicates that the response MAY be cached by any cache, even if it would normally be non-cacheable or cacheable only within a non- shared cache.

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

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

When the must-revalidate directive is present in a response received by a cache, that cache MUST NOT use the entry after it becomes stale to respond to a subsequent request without first revalidating it with the origin server

c.缓存控制:预检查(IE5 引入)

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

Defines an interval in seconds after which an entity must be checked for freshness. The check may happen after the user is shown the resource but ensures that on the next roundtrip the cached copy will be up-to-date.

d.Cache-Control: post-check(IE5 引入)

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

Defines an interval in seconds after which an entity must be checked for freshness prior to showing the user the resource.

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

e. Pragma: no-cache (to ensure backwards compatibility with HTTP/1.0)

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

When the no-cache directive is present in a request message, an application SHOULD forward the request toward the origin server even if it has a cached copy of what is being requested

f.过期

Expires entity-header 字段给出了响应被视为陈旧的日期/时间.

The Expires entity-header field gives the date/time after which the response is considered stale.

  • 我们从操作中返回文件

  • We return the file from the action

    return File(file, "mime/type", fileName);
    

  • 用户将看到一个打开/保存对话框

  • The user is presented with an Open/Save dialog box

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

    There are a half dozen other apps here that use the same headers to force Excel, CSV, PDF, Word, and a ton of other content at users and there's never been an issue.

    • 标题对于我们正在尝试做的事情是否正确?我们希望文件暂时存在(缓存),但总是被新版本替换,即使请求可能相同).

    在返回 FileResult 之前,在 action 方法中设置响应头.我让我的同事尝试创建一个继承自 FileResult 的新类,并改写 ExecuteResult 方法,以便它修改标题,然后执行 base.ExecuteResult() 代替 - 没有状态.

    The response headers are set in the action method before return a FileResult. I've asked my colleague to try creating a new class that inherits from FileResult and to instead override the ExecuteResult method so that it modifies the headers and then does base.ExecuteResult() instead -- no status on that.

    我有一种预感,0"的Expires"标头是罪魁祸首.根据这篇 W3C 文章,将其设置为"0"表示已经过期".我确实希望它过期,我只是不希望 IE 在处理它的应用程序有机会打开它之前将它从文件系统中删除.

    I have a hunch the "Expires" header of "0" is the culprit. According to this W3C article, setting it to "0" implies "already expired." I do want it to be expired, I just don't want IE to go removing it off of the filesystem before the application handling it gets a chance to open it.

    一如既往,谢谢!

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

    Upon further testing (using Fiddler to inspect the headers), we were seeing that the response headers we thought were getting set were not the ones being interpreted by the browser. Having not been familiar with the code myself, I was unaware of an underlying issue: the headers were getting stomped on outside of the action method.

    尽管如此,我还是要保留这个问题.仍然突出的是:Expires 标头的值是 0-1 之间似乎存在一些差异.如果有人可以声称 设计上的差异,就 IE 而言,我仍然希望听到它.不过,至于解决方案,上述标题确实按预期工作,并且在所有浏览器中将 Expires 值设置为 -1.

    Nonetheless, I'm going to leave this question open. Still outstanding is this: there seems to be some discrepancy between the Expires header having a value of 0 vs. -1. If anybody can lay claim to differences by design, in regards to IE, I would still like to hear about it. As for a solution though, the above headers do work as intended with the Expires value set to -1 in all browsers.

    帖子如何控制所有浏览器的网页缓存?详细描述了可以在所有情况下阻止缓存浏览器在设置 Expires = 0 的帮助下.我仍然不喜欢这个 0 vs -1 参数......

    The post How to control web page caching, across all browsers? describes in detail that caching can be prevented in all browsers with the help of setting Expires = 0. I'm still not sold on this 0 vs -1 argument...

    推荐答案

    我认为你应该使用

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

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

    设置 max-age=0 这意味着缓存重新验证(参见 此处).如果您要在标头中额外设置 ETag 以及一些您自定义的数据哈希校验和,则来自前一个请求的 ETag 将被发送到服务器.服务器可以返回数据,或者如果数据与之前完全相同,它可以返回空主体和 HttpStatusCode.NotModified 作为状态代码.在这种情况下,Web 浏览器将从本地浏览器缓存中获取数据.

    to set max-age=0 which means nothing more as the cache re-validating (see here). If you would be set additionally ETag in the header with some your custom checksum of hash from the data, the ETag from the previous request will be sent to the server. The server are able either to return the data or, in case that the data are exactly the same as before, it can return empty body and HttpStatusCode.NotModified as the status code. In the case the web browser will get the data from the local browser cache.

    我建议您使用 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.

    I recommend you to use Cache-Control: private which force two important things: 1) switch off caching the data on the proxy, which has sometimes very aggressive caching settings 2) it will allows the caching of the the data, but not permit sharing of the cache with another users. It can solve privacy problems because the data which you return to one user could be not allowed to read by another users. By the way the code HttpContext.Current.Response.Cache.SetMaxAge (new TimeSpan (0)) set Cache-Control: private, max-age=0 in the HTTP header by default. If you do want to use Cache-Control: public you can use SetCacheability (HttpCacheability.Public); to overwrite the behavior or use Headers.Set instead of Cache.SetMaxAge.

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

    If you have interest to study more caching options of HTTP protocol I would recommend you to read the caching tutorial.

    更新:我决定写一些更多的信息来澄清我的立场.对应于来自维基百科的信息,即使是像 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.

    UPDATED: I decide to write some more information to clear my position. Corresponds to the information from the Wikipedia even so old web browsers like Mosaic 2.7, Netscape 2.0 and Internet Explorer 3.0 supports March 1996, pre-standard of HTTP/1.1 described in RFC 2068. So I suppose (but not test it) that the old web browsers support max-age=0 HTTP header. In any way Netscape 2.06 and Internet Explorer 4.0 definitively supports HTTP 1.1.

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

    So you should ask you first: which HTML standards you use? Do you still use HTML 2.0 instead of more late HTML 3.2 published in January 1997? I suppose you use at least HTML 4.0 published in December 1997. So if you build your application at least in HTML 4.0, your site can be oriented on the web clients which supports HTTP 1.1 and ignore (don't support) the web clients which don't support HTTP 1.1.

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

    Now about other "Cache-Control" headers as "private, max-age=0". Including of the headers is in my opinion is pure paranoia. As I have some caching problem myself I tried also to include different other headers, but later after reading carefully the section 14.9 of RFC2616 I use only "Cache-Control: private, max-age=0".

    唯一可以额外讨论的Cache-Control"标头是我之前引用的第 14.9.4 节中描述的必须重新验证".这是引用:

    The only "Cache-Control" header which can be additionally discussed is "must-revalidate" described on the section 14.9.4 which I referenced before. Here is the quote:

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

    The must-revalidate directive is necessary to support reliable operation for certain protocol features. In all circumstances an HTTP/1.1 cache MUST obey the must-revalidate directive; in particular, if the cache cannot reach the origin server for any reason, it MUST generate a 504 (Gateway Timeout) response.

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

    Servers SHOULD send the must-revalidate directive if and only if failure to revalidate a request on the entity could result in incorrect operation, such as a silently unexecuted financial transaction. Recipients MUST NOT take any automated action that violates this directive, and MUST NOT automatically provide an unvalidated copy of the entity if revalidation fails.

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

    Although this is not recommended, user agents operating under severe connectivity constraints MAY violate this directive but, if so, MUST explicitly warn the user that an unvalidated response has been provided. The warning MUST be provided on each unvalidated access, and SHOULD require explicit user confirmation.

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

    Sometime if I have problem with Internet connection I see the empty page with "Gateway Timeout" message. It come from the the usage of "must-revalidate" directive. I don't think that "Gateway Timeout" message really help the user.

    因此,如果他在给老板打电话时听到忙碌"信号,那么他们更愿意启动自毁程序,应该在Cache-Control"标头中另外使用must-revalidate"指令.我推荐的其他人只使用Cache-Control: private, max-age=0",仅此而已.

    So the persons, how prefer to start self-destructive procedure if he hears "Busy" signal on the call to his boss, should additionally use "must-revalidate" directive in the "Cache-Control" header. Other persons I recommend just use "Cache-Control: private, max-age=0" and nothing more.

    这篇关于ASP.NET MVC 和 IE 缓存 - 操作响应头无效的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

  • 查看全文
    登录 关闭
    扫码关注1秒登录
    发送“验证码”获取 | 15天全站免登陆