协慌网

登录 贡献 社区

iOS 6 上的 Safari 缓存 $ .ajax 结果吗?

自升级到 iOS 6 以来,我们看到 Safari 的 Web 视图可以自由缓存$.ajax调用。这是在 PhoneGap 应用程序的上下文中,因此它使用 Safari WebView。我们的$.ajax调用是POST方法,我们将缓存设置为 false {cache:false} ,但仍然会发生这种情况。我们尝试手动向标头添加TimeStamp但它没有帮助。

我们做了更多研究,发现 Safari 只返回具有静态功能签名并且不会因呼叫而改变的 Web 服务的缓存结果。例如,想象一个名为:

getNewRecordID(intRecordType)

该函数反复接收相同的输入参数,但每次返回的数据应该不同。

一定是苹果急于让 iOS 6 拉链令人印象深刻,他们对缓存设置感到满意。还有其他人在 iOS 6 上看到过这种行为吗?如果是这样,究竟是什么导致了它?


我们发现的解决方法是将函数签名修改为如下所示:

getNewRecordID(intRecordType, strTimestamp)

然后总是传入一个TimeStamp参数,并在服务器端丢弃该值。这解决了这个问题。我希望这能帮助其他一些在这个问题上花费 15 个小时的穷人,就像我一样!

答案

经过一番调查后,发现 iOS6 上的 Safari 将缓存没有 Cache-Control 标头甚至 “Cache-Control:max-age = 0” 的 POST。

我发现阻止此缓存在全局级别发生的唯一方法是设置 “Cache-Control:no-cache”,而不是将随机查询字符串发送到服务调用结束。

所以:

  • No Cache-Control 或 Expires headers = iOS6 Safari 将缓存
  • Cache-Control max-age = 0,立即 Expires = iOS6 Safari 将缓存
  • Cache-Control:no-cache = iOS6 Safari 不会缓存

我怀疑 Apple 在第 9.5 节关于 POST 的 HTTP 规范中利用了这一点:

除非响应包含适当的 Cache-Control 或 Expires 头字段,否则对此方法的响应不可缓存。但是,303(请参阅其他)响应可用于指示用户代理检索可缓存资源。

所以理论上你可以缓存 POST 响应...... 谁知道。但直到现在还没有其他浏览器制造商认为这是一个好主意。但是,如果没有设置 Cache-Control 或 Expires 标头,那么这不会考虑缓存,只有在有一些设置时才会考虑。所以一定是个 bug。

下面是我在我的 Apache 配置的正确位置使用的目标我的整个 API,因为它实际上我实际上并不想缓存任何东西,甚至得到。我不知道的是如何为 POST 设置它。

Header set Cache-Control "no-cache"

更新:刚刚注意到我没有指出它只是在 POST 是相同的时候,所以更改任何 POST 数据或 URL,你就没事了。所以你可以像其他地方一样在网址或一些 POST 数据中添加一些随机数据。

更新:如果你想在 Apache 中这样做,你可以将 “无缓存” 限制为 POST:

SetEnvIf Request_Method "POST" IS_POST
Header set Cache-Control "no-cache" env=IS_POST

我希望这可以对其他开发人员在这个问题上猛烈抨击。我发现以下任何一种情况都会阻止 iOS 6 上的 Safari 缓存 POST 响应:

  • 在请求标头中添加 [cache-control:no-cache]
  • 添加可变 URL 参数,例如当前时间
  • 在响应头中添加 [pragma:no-cache]
  • 在响应头中添加 [cache-control:no-cache]

我的解决方案是我的 Javascript 中的以下内容(我的所有 AJAX 请求都是 POST)。

$.ajaxSetup({
    type: 'POST',
    headers: { "cache-control": "no-cache" }
});

我还将 [pragma:no-cache] 标头添加到我的许多服务器响应中。

如果你使用上面的解决方案,请注意任何 $ .ajax()调用你设置为 global:false 将不使用 $ .ajaxSetup()中指定的设置,因此你需要再次添加标题。

假设您正在使用 jQuery,所有 Web 服务请求的简单解决方案:

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
    // you can use originalOptions.type || options.type to restrict specific type of requests
    options.data = jQuery.param($.extend(originalOptions.data||{}, { 
      timeStamp: new Date().getTime()
    }));
});

在这里阅读有关 jQuery prefilter 调用的更多信息。

如果您不使用 jQuery,请检查您选择的库的文档。它们可能具有类似的功能。