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

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

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

 getNewRecordID(intRecordType) 

这个函数一次又一次地接收相同的输入参数,但是它返回的数据应该每次都不一样。

必须在苹果急于使iOS 6 zip令人印象深刻,他们对缓存设置太高兴了。 有没有其他人在iOS 6上看到这种行为? 如果是这样,究竟是什么原因造成的呢?


我们找到的解决方法是修改函数签名是这样的:

 getNewRecordID(intRecordType, strTimestamp) 

然后总是传递一个TimeStamp参数,并在服务器端丢弃该值。 这可以解决这个问题。 我希望这能帮助像我这样在这个问题上花费15个小时的其他可怜的灵魂!

经过一番调查,事实证明,iOS6上的Safari会缓存没有Cache-Control头或甚至“Cache-Control:max-age = 0”的POST。

我发现阻止这种缓存发生在全局级别的唯一方法是设置“Cache-Control:no-cache”,而不是将随机querystrings打乱到服务调用的最后。

所以:

  • 没有缓存控制或过期头= iOS6的Safari将缓存
  • 缓存控制最大年龄= 0,即时过期= iOS6的Safari将缓存
  • 缓存控制:no-cache = iOS6 Safari不会缓存

我怀疑苹果公司正在利用HTTP规范第9.5节中的关于POST的内容:

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

所以理论上你可以缓存POST回复……谁知道。 但没有其他浏览器制造商曾经认为这是一个好主意,直​​到现在。 但是,如果没有设置Cache-Control或Expires标头,那么这并不代表缓存。 所以它一定是一个错误。

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

 Header set Cache-Control "no-cache" 

更新:只是注意到,我没有指出,只有当POST是相同的,所以更改任何POST数据或URL,你很好。 所以,你可以在其他地方提到的只是添加一些随机数据的URL或一些POST数据。

更新:如果你希望在Apache中这样做,你可以将“no-cache”限制为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]头添加到我的许多服务器响应中。

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

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

 $.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,请检查你所选库的文档。 他们可能有类似的功能。

我有一个web应用程序从ASP.NET webservice获取数据的同样的问题

这对我工作:

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

PhoneGap应用程序中也有这个问题。 我通过以下方式使用JavaScript函数getTime()来解决它:

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

我花了几个小时弄清楚了这一点。 苹果公司会通知开发者这个缓存问题。

最后,我已经解决了我的上传问题。

在JavaScript中:

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

在PHP中 :

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

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

如何解决它:有各种方法来防止缓存请求。 推荐的方法是添加一个无缓存头。 这是如何完成的。

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

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

。净

 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. 

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

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

只要把它放在你的JavaScript代码的某个地方(在jQuery加载之后,最好在你做AJAX请求之前),它应该有所帮助。

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

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

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

您还可以通过修改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; } 

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

为了解决添加到主屏幕上的WebApps的这个问题,需要遵循顶级投票解决方法。 缓存需要在网络服务器上关闭,以防止新的请求被缓存,并且需要将一些随机的输入添加到每个发布请求中,以便已经被缓存的请求通过。 请参考我的帖子:

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

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

这是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); 

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

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

使用iPad 4 / iOS 6 适合我的东西:

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

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

添加缓存:假的我的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); 

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

对于那些使用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() { } } 

我能够解决我的问题,通过使用$ .ajaxSetup的组合,并附加一个时间戳到我的职位的网址(而不是post参数/正文)。 这基于以前的答案的建议

 $(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'); } } }); }); }); 

我认为你已经解决了你的问题,但让我分享一个关于网页缓存的想法。

确实,你可以在你使用的每种语言,服务器端,客户端添加许多头文件,并且你可以使用许多其他的技巧来避免网页缓存,但总是认为你永远不知道客户端从哪里连接到你的服务器,你永远不知道他是否使用酒店“热点”连接,使用鱿鱼或其他缓存产品。

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

例如:

 /ajax_helper.php?ts=3211321456 

然后,您必须通过的每个缓存管理器都不会在缓存存储库中找到相同的URL,然后重新下载页面内容。

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

getNewRecordID(intRecordType,strTimestamp),然后总是传入TimeStamp参数,并在服务器端丢弃该值。 这可以解决这个问题。

根据应用程序的不同,您可以使用Safari>高级> Web Inspector在iOS 6中解决问题,以便在此情况下有所帮助。

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

更新到iOS6后,清除iPhone上的网站数据,包括使用Web视图特定于应用程序。 只有一个应用程序有一个问题,这就解决了它在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电话

我发现一个解决方法,让我好奇它为什么它的作品。 在阅读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,我会得到更新的内容。

虽然我的登录页面和注册页面在Firefox,IE和Chrome中都很有魅力,但是在iOS和OSX的Safari浏览器中,我一直在努力解决这个问题,几个月前,我在SO上找到了一个解决方法。

 <body onunload=""> 

或通过JavaScript

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

这是一件有点丑陋的事情,但有一段时间。

我不知道为什么,但返回null的onunload事件页面不缓存在Safari中。

在Ruby的Sinatra

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

只有在IIS中添加了pragma:no-cache头之后,才能与ASP.NET一起工作。 Cache-Control: no-cache是不够的。