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

use*_*978 1057 javascript ajax jquery caching mobile-safari

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

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

getNewRecordID(intRecordType)
Run Code Online (Sandbox Code Playgroud)

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

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


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

getNewRecordID(intRecordType, strTimestamp)
Run Code Online (Sandbox Code Playgroud)

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

Kie*_*ran 444

经过一番调查后,发现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"
Run Code Online (Sandbox Code Playgroud)

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

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

SetEnvIf Request_Method "POST" IS_POST
Header set Cache-Control "no-cache" env=IS_POST
Run Code Online (Sandbox Code Playgroud)

  • 您引用的HTTP规范部分并不能证明iOS 6的缓存行为是正确的.默认行为应该是不缓存POST响应(即未定义"Cache-Control"标头时).该行为违反了规范,应该被视为一个错误.任何构建xml/json api Web服务的人都应该用"Cache-control:no-cache"修饰他们的POST响应来解决这个问题. (138认同)
  • POST请求是非幂等的,这意味着它们不应该被缓存_unless_响应特别建议通过其响应头来这样做. (39认同)
  • 我看到Apple正在采用这种方式,但即使我们的响应中没有包含任何Cache-Control或Expires标头,我们也会看到对POST请求的缓存响应.这个实例iOS6不应该缓存并发送每个请求.这不会发生. (6认同)
  • 正如大卫所说,这明显违反了你所引用的判决.如果没有"Cache-Control或Expires头字段",则显然不包括适当的此类头.然而,你自己的调查显示它在那种情况下缓存.请编辑你的答案. (4认同)
  • 有谁知道结果在设备上缓存了多长时间?我试过杀死safari并重新启动我的手机,但它仍然被缓存.我知道它可以清除浏览器缓存,但我想知道在它消失之前曾经遇到问题的用户需要多长时间.不是每个人都会想到清除他们的缓存...... (3认同)
  • 我发现更改POST数据不会扰乱缓存 - 它似乎完全基于url. (2认同)

小智 146

我希望这可以对其他开发人员在这个问题上猛烈抨击.我发现以下任何一种情况都会阻止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" }
});
Run Code Online (Sandbox Code Playgroud)

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

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

  • 这是错误的正确解决方案.问题是iOS 6将从它的缓存服务POST请求,而不是将它们发送到服务器.该错误并不是它缓存来自POST请求的响应(允许).如果您仍希望响应从缓存中检索的POST请求,以便对该URI进行后续GET请求,请使用此解决方案. (4认同)
  • 这对我有用,但我不明白怎么做.我已经在我的ajaxSetup中指定了cache:false,并查看了请求标头,它归结为Cache-Control:no-cache和Pragma:no-cache - 但它仍将缓存在iPad上.然后,当我将标题:{"cache-control":"no-cache"}添加到a​​jaxSetup中时,它将Cache-Control标头加倍为"no-cache,no-cache" - 并停止缓存.这里发生了什么事? (2认同)

Baz*_*nga 67

假设您正在使用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()
    }));
});
Run Code Online (Sandbox Code Playgroud)

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

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

  • 它对我不起作用,服务器响应:"无效的原始JSON:timeStamp"asp.net/iis 7.5 (3认同)
  • 怎么样$ .ajax({"cache":false ...})?它会附加一个_ = [TIMESTAMP]吗?(我没有这样的设备来测试它) (3认同)

Bas*_*vis 43

我在PhoneGap应用程序中也遇到过这个问题.我通过getTime()以下方式使用JavaScript函数解决了它:

var currentTime = new Date();
var n = currentTime.getTime();
postUrl = "http://www.example.com/test.php?nocache="+n;
$.post(postUrl, callbackFunction);
Run Code Online (Sandbox Code Playgroud)

我浪费了几个小时搞清楚这一点.Apple通知开发人员这个缓存问题本来不错.


小智 42

我从webappser从ASP.NET webservice获取数据时遇到了同样的问题

这对我有用:

public WebService()
{
    HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    ...
}
Run Code Online (Sandbox Code Playgroud)

  • 非常感谢你!我疯狂地想弄清楚为什么iPhone的表现与其他平台差别很大.这种特定于ASP.NET的解决方案为我节省了大量时间. (2认同)

gok*_*eci 24

最后,我找到了上传问题的解决方案.

在JavaScript中:

var xhr = new XMLHttpRequest();
xhr.open("post", 'uploader.php', true);
xhr.setRequestHeader("pragma", "no-cache");
Run Code Online (Sandbox Code Playgroud)

PHP中:

header('cache-control: no-cache');
Run Code Online (Sandbox Code Playgroud)


kir*_*nvj 15

从我自己的博客文章iOS 6.0缓存Ajax POST请求:

如何解决:有多种方法可以防止缓存请求.建议的方法是添加no-cache标头.这就是它的完成方式.

jQuery的:

检查iOS 6.0并设置Ajax标头如下:

$.ajaxSetup({ cache: false });
Run Code Online (Sandbox Code Playgroud)

ZeptoJS:

检查iOS 6.0并设置Ajax标头如下:

$.ajax({
    type: 'POST',
    headers : { "cache-control": "no-cache" },
    url : ,
    data:,
    dataType : 'json',
    success : function(responseText) {…}
Run Code Online (Sandbox Code Playgroud)

服务器端

Java的:

httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
Run Code Online (Sandbox Code Playgroud)

确保在将任何数据发送到客户端之前将其添加到页面顶部.

.净

Response.Cache.SetNoStore();
Run Code Online (Sandbox Code Playgroud)

要么

Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
Run Code Online (Sandbox Code Playgroud)

PHP

header('Cache-Control: no-cache, no-store, must-revalidate'); // HTTP 1.1.
header('Pragma: no-cache'); // HTTP 1.0.
Run Code Online (Sandbox Code Playgroud)

  • .NET的良好无缓存属性http://stackoverflow.com/questions/10011780/prevent-caching-in-asp-net-mvc-3 (2认同)

Jon*_*han 7

这个JavaScript代码段适用于jQuery和jQuery Mobile:

$.ajaxSetup({
    cache: false,
    headers: {
        'Cache-Control': 'no-cache'
    }
});
Run Code Online (Sandbox Code Playgroud)

只需将它放在JavaScript代码中的某个位置(在加载jQuery之后,最好在执行AJAX请求之前)它应该有所帮助.


Sam*_*les 6

您还可以通过修改jQuery Ajax函数来解决此问题,方法是执行以下操作(从1.7.1开始)到Ajax函数的顶部(函数从第7212行开始).此更改将为所有POST请求激活jQuery的内置反缓存功能.

(完整的脚本可在http://dl.dropbox.com/u/58016866/jquery-1.7.1.js.)

在第7221行下方插入:

if (options.type === "POST") {
    options.cache = false;
}
Run Code Online (Sandbox Code Playgroud)

然后修改以下内容(从第〜7497行开始).

if (!s.hasContent) {
    // If data is available, append data to URL
    if (s.data) {
        s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
        // #9682: remove data so that it's not used in an eventual retry
        delete s.data;
    }

    // Get ifModifiedKey before adding the anti-cache parameter
    ifModifiedKey = s.url;

    // Add anti-cache in URL if needed
    if (s.cache === false) {
        var ts = jQuery.now(),
        // Try replacing _= if it is there
        ret = s.url.replace(rts, "$1_=" + ts);

        // If nothing was replaced, add timestamp to the end.
        s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
    }
}
Run Code Online (Sandbox Code Playgroud)

至:

// More options handling for requests with no content
if (!s.hasContent) {
    // If data is available, append data to URL
    if (s.data) {
        s.url += (rquery.test(s.url) ? "&" : "?") + s.data;
        // #9682: remove data so that it's not used in an eventual retry
        delete s.data;
    }

    // Get ifModifiedKey before adding the anti-cache parameter
    ifModifiedKey = s.url;
}

// Add anti-cache in URL if needed
if (s.cache === false) {
    var ts = jQuery.now(),
    // Try replacing _= if it is there
    ret = s.url.replace(rts, "$1_=" + ts);

    // If nothing was replaced, add timestamp to the end.
    s.url = ret + ((ret === s.url) ? (rquery.test(s.url) ? "&" : "?") + "_=" + ts : "");
}
Run Code Online (Sandbox Code Playgroud)

  • 这不是改变jQuery的好方法,也不是你不拥有的任何代码.(每当你想要更新版本时,你必须再次进行更改.(或者另一个开发人员更新,程序不起作用)) (4认同)

fba*_*der 5

为了解决添加到主屏幕的 Web 应用程序的此问题,需要遵循两个投票最多的解决方法。需要在网络服务器上关闭缓存,以防止继续缓存新的请求,并且需要将一些随机输入添加到每个 post 请求中,以便已缓存的请求能够通过。请参考我的帖子:

iOS6 - 有没有办法清除添加到主屏幕的 web 应用程序的缓存 ajax POST 请求?

警告:对于通过向请求添加时间戳而不关闭服务器上的缓存来实现解决方法的任何人。如果您的应用程序被添加到主屏幕,现在每个帖子响应都将被缓存,清除 safari 缓存并不会清除它,而且它似乎不会过期。除非有人有办法清除它,否则这看起来像是潜在的内存泄漏!


Lar*_*ahl 5

GWT-RPC服务的快速解决方法是将其添加到所有远程方法:

getThreadLocalResponse().setHeader("Cache-Control", "no-cache");
Run Code Online (Sandbox Code Playgroud)


rem*_*der 5

这是Baz1nga答案的更新.因为options.data不是一个对象而是一个字符串,我只是使用连接时间戳:

$.ajaxPrefilter(function (options, originalOptions, jqXHR) {
  if (originalOptions.type == "post" || options.type == "post") {

    if (options.data && options.data.length)
      options.data += "&";
    else
      options.data = "";

    options.data += "timeStamp=" + new Date().getTime();
  }
});
Run Code Online (Sandbox Code Playgroud)


Bri*_*den 5

对于我的 iPad 4/iOS 6不起作用的事情:

我的请求包含:Cache-Control:no-cache

//asp.net's:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache)
Run Code Online (Sandbox Code Playgroud)

添加 cache: false 到我的 jQuery ajax 调用

 $.ajax(
        {
            url: postUrl,
            type: "POST",
            cache: false,
            ...
Run Code Online (Sandbox Code Playgroud)

只有这样才能达到目的:

var currentTime = new Date();
var n = currentTime.getTime();
postUrl = "http://www.example.com/test.php?nocache="+n;
$.post(postUrl, callbackFunction);
Run Code Online (Sandbox Code Playgroud)