首页 文章

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

提问于
浏览
1040

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

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

getNewRecordID(intRecordType)

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

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


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

getNewRecordID(intRecordType, strTimestamp)

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

25 回答

  • 5

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

    getThreadLocalResponse().setHeader("Cache-Control", "no-cache");
    
  • 22

    这是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();
      }
    });
    
  • 1

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

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

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

  • 143

    在Ruby的Sinatra

    before '*' do
      if env['REQUEST_METHOD'] == 'POST'
        headers 'Cache-Control' => 'no-cache, no-store, must-revalidate'
      end
    end
    
  • 0

    虽然添加cache-buster参数以使请求看起来不同似乎是一个可靠的解决方案,但我建议反对它,因为它会伤害任何依赖于实际缓存的应用程序 . 使API输出正确的标头是最好的解决方案,即使这比向调用者添加缓存触发器要困难一些 .

  • 4

    为了解决添加到主屏幕的WebApps的这个问题,需要遵循两个最高投票的解决方法 . 需要在Web服务器上关闭缓存,以防止新请求被缓存,并且需要向每个post请求添加一些随机输入,以便已经缓存的请求通过 . 请参考我的帖子:

    iOS6 - Is there a way to clear cached ajax POST requests for webapp added to home screen?

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

  • 0

    假设您正在使用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调用here的更多信息 .

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

  • 0

    我的解决方法在ASP.NET(pagemethods,webservice等)

    protected void Application_BeginRequest(object sender, EventArgs e)
    {
        Response.Cache.SetCacheability(HttpCacheability.NoCache);
    }
    
  • 5

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

    (完整脚本位于 http://dl.dropbox.com/u/58016866/jquery-1.7.1.js . )

    在第7221行下方插入:

    if (options.type === "POST") {
        options.cache = false;
    }
    

    然后修改以下内容(从第〜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 : "");
        }
    }
    

    至:

    // 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 : "");
    }
    
  • 1

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

    $.ajaxSetup({
        cache: false,
        headers: {
            'Cache-Control': 'no-cache'
        }
    });
    

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

  • 14

    经过一番调查后发现,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中这样做,你可以将“no-cache”限制为POST:

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

    虽然我的登录和注册页面就像Firefox,IE和Chrome中的魅力......我在Safari for IOS中一直在努力解决这个问题和OSX,几个月前我找到了SO的解决方法 .

    <body onunload="">
    

    或通过JavaScript

    <script type="text/javascript">
    window.onunload = function(e){
        e.preventDefault();
        return;
    };
    </script>
    

    这有点难看,但有一段时间有效 .

    我不知道为什么,但是返回null到 onunload 事件页面不会在Safari中缓存 .

  • 41

    我希望这可以对其他开发人员在这个问题上猛烈抨击 . 我发现以下任何一种情况都会阻止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()中指定的设置,因此你需要再次添加 Headers .

  • 65

    我从webapp服务器获取数据的webapp也遇到了同样的问题

    这对我有用:

    public WebService()
    {
        HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        ...
    }
    
  • 0

    我能够通过使用$ .ajaxSetup的组合并将时间戳附加到我的帖子的url(而不是post参数/ body)来解决我的问题 . 这基于以前答案的建议

    $(document).ready(function(){
        $.ajaxSetup({ type:'POST', headers: {"cache-control","no-cache"}});
    
        $('#myForm').submit(function() {
            var data = $('#myForm').serialize();
            var now = new Date();
            var n = now.getTime();
            $.ajax({
                type: 'POST',
                url: 'myendpoint.cfc?method=login&time='+n,
                data: data,
                success: function(results){
                    if(results.success) {
                        window.location = 'app.cfm';
                    } else {
                        console.log(results);
                        alert('login failed');
                    }
                }
            });
        });
    });
    
  • 7

    只有在IIS中添加 pragma:no-cache 标头后,才能使用ASP.NET . Cache-Control: no-cache 还不够 .

  • 0

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

    在JavaScript中:

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

    PHP

    header('cache-control: no-cache');
    
  • 437

    DID NOT WORK 对我来说与iPad 4 / iOS 6的关系:

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

    //asp.net's:
    HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache)
    

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

    $.ajax(
            {
                url: postUrl,
                type: "POST",
                cache: false,
                ...
    

    只有这样才能解决问题:

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

    根据应用程序的不同,您可以使用Safari>高级> Web Inspector在iOS 6中解决问题,这对于这种情况很有帮助 .

    在Mac上将手机连接到Safari,然后使用开发人员菜单来解决网络应用的问题 .

    更新到iOS6后清除iPhone上的网站数据,包括使用Web View特定于应用程序 . 只有一个应用程序有问题,这在IOS6 Beta测试期间解决了它,因为那时没有真正的问题 .

    您可能还需要查看您的应用程序,如果在自定义应用程序中的WebView中,请查看NSURLCache .

    https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003754

    我想这取决于你的问题,实施等的真实性质 .

    参考:$ .ajax电话

  • 1

    我想你已经解决了你的问题,但让我分享一下关于网络缓存的想法 .

    确实,您可以使用您使用的每种语言,服务器端,客户端添加许多 Headers ,并且您可以使用许多其他技巧来避免Web缓存,但始终认为您永远无法知道客户端连接到服务器的位置,你永远不知道他是否使用了使用Squid或其他缓存产品的酒店“热点”连接 .

    如果用户正在使用代理来隐藏他的真实位置等......避免缓存的唯一方法是请求中的时间戳,如果未使用的话 .

    例如:

    /ajax_helper.php?ts=3211321456
    

    然后,您必须传递的每个缓存管理器都没有在缓存存储库中找到相同的URL并重新下载页面内容 .

  • 41

    我建议修改函数签名的解决方法是这样的:

    getNewRecordID(intRecordType,strTimestamp)然后总是传入一个TimeStamp参数,并且只丢弃服务器端的那个值 . 这解决了这个问题 .

  • 6

    我找到了一个解决方法,让我对它为何起作用感到好奇 . 在阅读Tadej关于ASP.NET Web服务的答案之前,我试图提出一些可行的方法 .

    我并不是说这是一个很好的解决方案,但我只想在这里记录下来 .

    主页面:包含一个JavaScript函数,checkStatus() . 该方法调用另一个使用jQuery AJAX调用来更新html内容的方法 . 我使用setInterval来调用checkStatus() . 当然,我遇到了缓存问题 .

    解决方案:使用另一个页面来调用更新 .

    在主页面上,我设置了一个布尔值变量,runUpdate,并将以下内容添加到body标记:

    <iframe src="helper.html" style="display: none; visibility: hidden;"></iframe>
    

    在helper.html中:

    <meta http-equiv="refresh" content="5">
    <script type="text/javascript">
        if (parent.runUpdate) { parent.checkStatus(); }
    </script>
    

    因此,如果从主页调用checkStatus(),我将获得缓存的内容 . 如果我从子页面调用checkStatus,我会获得更新的内容 .

  • 2

    来自我自己的博客文章iOS 6.0 caching Ajax POST requests

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

    jQuery:

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

    $.ajaxSetup({ cache: false });
    

    ZeptoJS:

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

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

    服务器端

    Java:

    httpResponse.setHeader("Cache-Control", "no-cache, no-store, must-revalidate");
    

    在将任何数据发送到客户端之前,请务必在页面顶部添加此项 .

    .NET

    Response.Cache.SetNoStore();
    

    要么

    Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
    

    PHP

    header('Cache-Control: no-cache, no-store, must-revalidate'); // HTTP 1.1.
    header('Pragma: no-cache'); // HTTP 1.0.
    
  • -1

    对于那些使用 Struts 1 的人,我就是这样解决问题的方法 .

    web.xml

    <filter>
        <filter-name>SetCacheControl</filter-name>
        <filter-class>com.example.struts.filters.CacheControlFilter</filter-class>
    </filter>
    
    <filter-mapping>
        <filter-name>SetCacheControl</filter-name>
        <url-pattern>*.do</url-pattern>
        <http-method>POST</http-method>
    </filter-mapping>
    

    com.example.struts.filters.CacheControlFilter.js

    package com.example.struts.filters;
    
    import java.io.IOException;
    import java.util.Date;
    import javax.servlet.*;
    import javax.servlet.http.HttpServletResponse;
    
    public class CacheControlFilter implements Filter {
    
            public void doFilter(ServletRequest request, ServletResponse response,
                         FilterChain chain) throws IOException, ServletException {
    
            HttpServletResponse resp = (HttpServletResponse) response;
            resp.setHeader("Expires", "Mon, 18 Jun 1973 18:00:00 GMT");
            resp.setHeader("Last-Modified", new Date().toString());
            resp.setHeader("Cache-Control", "no-store, no-cache, must-revalidate, max-age=0, post-check=0, pre-check=0");
            resp.setHeader("Pragma", "no-cache");
    
            chain.doFilter(request, response);
        }
    
        public void init(FilterConfig filterConfig) throws ServletException {
        }
    
        public void destroy() {
        }
    
    }
    
  • 3

    这是GWT-RPC的解决方案

    class AuthenticatingRequestBuilder extends RpcRequestBuilder 
    {
           @Override
           protected RequestBuilder doCreate(String serviceEntryPoint) 
           {
                   RequestBuilder requestBuilder = super.doCreate(serviceEntryPoint);           
                   requestBuilder.setHeader("Cache-Control", "no-cache");
    
                   return requestBuilder;
           }
    }
    
    AuthenticatingRequestBuilder builder = new AuthenticatingRequestBuilder();
    ((ServiceDefTarget)myService).setRpcRequestBuilder(builder);
    

相关问题