提问人:user1684978 提问时间:9/20/2012 最后编辑:Sreeram Nairuser1684978 更新时间:7/7/2023 访问量:144976
iOS 6 上的 Safari 是否缓存 $.ajax 结果?
Is Safari on iOS 6 caching $.ajax results?
问:
自从升级到 iOS 6 以来,我们看到 Safari 的 Web 视图可以自由地缓存调用。这是在PhoneGap应用程序的上下文中,因此它使用Safari WebView。我们的调用是方法,我们将缓存设置为 false ,但这种情况仍在发生。我们尝试手动将 a 添加到标题中,但没有帮助。$.ajax
$.ajax
POST
{cache:false}
TimeStamp
我们做了更多的研究,发现 Safari 只返回 Web 服务的缓存结果,这些 Web 服务具有静态的函数签名,并且不会随调用而变化。例如,想象一个函数,称为:
getNewRecordID(intRecordType)
此函数一遍又一遍地接收相同的输入参数,但它每次返回的数据都应该不同。
一定是苹果急于让iOS 6拉链,令人印象深刻,他们对缓存设置太满意了。有没有人在 iOS 6 上看到过这种行为?如果是这样,究竟是什么原因造成的?
我们发现的解决方法是将函数签名修改为如下所示:
getNewRecordID(intRecordType, strTimestamp)
然后始终传入一个参数,并在服务器端丢弃该值。这可以解决此问题。TimeStamp
答:
我只是在PhoneGap应用程序中也遇到了这个问题。我通过以下方式使用 JavaScript 函数解决了它:getTime()
var currentTime = new Date();
var n = currentTime.getTime();
postUrl = "http://www.example.com/test.php?nocache="+n;
$.post(postUrl, callbackFunction);
我浪费了几个小时来弄清楚这一点。如果 Apple 将此缓存问题通知开发人员,那就太好了。
评论
{cache:false}
$.post()
$.ajaxSetup()
$.ajaxPrefilter()
function send_ajax(my_data,refresh)
经过一番调查,事实证明 iOS6 上的 Safari 将缓存没有 Cache-Control 标头甚至“Cache-Control: max-age=0”的 POST。
我发现防止这种缓存在全局级别发生而不必在服务调用结束时破解随机查询字符串的唯一方法是设置“Cache-Control: no-cache”。
所以:
- 没有 Cache-Control 或 Expires 标头 = iOS6 Safari 将缓存
- Cache-Control max-age=0 和立即过期 = iOS6 Safari 将缓存
- Cache-Control: no-cache = iOS6 Safari 不会缓存
我怀疑 Apple 正在利用 HTTP 规范中关于 POST 的第 9.5 节中的这一点:
对此方法的响应是不可缓存的,除非响应 包括相应的 Cache-Control 或 Expires 标头字段。然而 303(请参阅其他)响应可用于将用户代理定向到 检索可缓存资源。
所以从理论上讲,你可以缓存 POST 响应......谁知道呢。但直到现在,还没有其他浏览器制造商认为这是一个好主意。但是,当没有设置 Cache-Control 或 Expires 标头时,这不会考虑缓存,只有当设置了一些标头时。所以它一定是一个错误。
以下是我在 Apache 配置的正确部分中使用的内容,以针对我的整个 API,因为碰巧我实际上不想缓存任何东西,甚至不想缓存。我不知道的是如何仅为 POST 设置它。
Header set Cache-Control "no-cache"
更新:刚刚注意到我没有指出只有当 POST 相同时才有,所以更改任何 POST 数据或 URL 就可以了。因此,正如其他地方所述,您只需向 URL 添加一些随机数据或一些 POST 数据即可。
更新:如果您希望在 Apache 中这样,您可以将“无缓存”限制为 POST:
SetEnvIf Request_Method "POST" IS_POST
Header set Cache-Control "no-cache" env=IS_POST
评论
我在 Web 应用程序从 Web 服务获取数据时遇到了同样的问题 ASP.NET
这对我有用:
public WebService()
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
...
}
评论
针对所有 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 预过滤器调用的更多信息。
如果您没有使用 jQuery,请查看所选库的文档。它们可能具有类似的功能。
评论
Depending on the app you can trouble shoot the issue now in iOS 6 using Safari>Advanced>Web Inspector so that is helpful with this situation.
Connect the phone to Safari on a Mac an then use the developer menu to trouble shoot the web app.
Clear the website data on the iPhone after update to iOS6, including specific to the app using a Web View. Only one app had an issue and this solved it during IOS6 Beta testing way back, since then no real problems.
You may need to look at your app as well, check out NSURLCache if in a WebView in a custom app.
I guess depending on the true nature of your problem, implementation, etc. ..
Ref: $.ajax calls
评论
Finally, I've a solution to my uploading problem.
In JavaScript:
var xhr = new XMLHttpRequest();
xhr.open("post", 'uploader.php', true);
xhr.setRequestHeader("pragma", "no-cache");
In PHP:
header('cache-control: no-cache');
虽然添加 cache-buster 参数以使请求看起来不同似乎是一个可靠的解决方案,但我建议不要这样做,因为它会损害任何依赖于实际缓存的应用程序。使 API 输出正确的标头是最好的解决方案,即使这比向调用方添加缓存破坏器稍微困难一些。
评论
我在 ASP.NET 中的解决方法(页面方法、网络服务等)
protected void Application_BeginRequest(object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
}
只有在 IIS 中添加标头后,它才适用于 ASP.NET。 还不够。pragma:no-cache
Cache-Control: no-cache
您还可以通过修改 jQuery Ajax 函数来解决此问题,方法是对 Ajax 函数的顶部执行以下操作(从 1.7.1 开始)(函数从第 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 : "");
}
评论
jQuery.ajaxPrefiler
为了解决添加到主屏幕的 WebApps 的此问题,需要遵循两个投票最高的解决方法。需要在 Web 服务器上关闭缓存,以防止将来缓存新请求,并且需要向每个 POST 请求添加一些随机输入,以便已经缓存的请求通过。请参考我的帖子:
iOS6 - 有没有办法清除添加到主屏幕的 Web 应用程序的缓存 ajax POST 请求?
警告:通过在未关闭服务器上的缓存的情况下向请求添加时间戳来实施变通方法的任何人。如果您的应用程序已添加到主屏幕,则现在将缓存每个帖子响应,清除 safari 缓存不会清除它,并且它似乎不会过期。除非有人有办法清除它,否则这看起来像是潜在的内存泄漏!
评论
我发现了一种解决方法,让我好奇它为什么有效。在阅读 Tadej 关于 Web 服务的回答之前 ASP.NET 我试图想出一些可行的方法。
我并不是说这是一个很好的解决方案,但我只是想在这里记录下来。
main page:包含一个 JavaScript 函数 checkStatus()。该方法调用另一个方法,该方法使用 jQuery AJAX 调用来更新 html 内容。我使用 setInterval 调用 checkStatus()。当然,我遇到了缓存问题。
解决方案:使用另一个页面调用更新。
在主页上,我设置了一个布尔变量 runUpdate,并将以下内容添加到 body 标签中:
<iframe src="helper.html" style="display: none; visibility: hidden;"></iframe>
在帮助程序.html中:
<meta http-equiv="refresh" content="5">
<script type="text/javascript">
if (parent.runUpdate) { parent.checkStatus(); }
</script>
因此,如果从主页调用 checkStatus(),我会得到缓存的内容。如果我从子页面调用 checkStatus,我会得到更新的内容。
这就是 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);
我希望这对其他开发人员在这个问题上撞墙有用。我发现以下任何一种情况都会阻止 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() 中指定的设置,因此您需要再次添加标头。
评论
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();
}
});
评论
从我自己的博客文章 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) {…}
##Server 面
###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.
评论
对于那些使用的人,这是我解决问题的方法。Struts 1
网络.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 的组合并将时间戳附加到我的帖子的 url(而不是帖子参数/正文)来解决我的问题。这是基于先前答案的建议
$(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');
}
}
});
});
});
此 JavaScript 代码段非常适合 jQuery 和 jQuery Mobile:
$.ajaxSetup({
cache: false,
headers: {
'Cache-Control': 'no-cache'
}
});
只需将它放在 JavaScript 代码中的某个位置(在加载 jQuery 之后,最好在执行 AJAX 请求之前),它应该会有所帮助。
在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);
评论
$.ajax
cache: false
_=Date.prototype.getTime()
我想你已经解决了你的问题,但让我分享一个关于网络缓存的想法。
诚然,你可以用你使用的每种语言添加许多标头,服务器端、客户端,你可以使用许多其他技巧来避免网络缓存,但总是认为你永远无法知道客户端从哪里连接到你的服务器,你永远不知道他是否正在使用使用 Squid 或其他缓存产品的酒店“热点”连接。
如果用户使用代理来隐藏他的真实位置等......避免缓存的真正唯一方法是请求中的时间戳,如果未使用。
例如:
/ajax_helper.php?ts=3211321456
然后,您必须传递的每个缓存管理器都没有在缓存存储库中找到相同的URL,然后重新下载页面内容。
评论
$.ajax
{cache:false}
虽然我的登录和注册页面在 Firefox、IE 和 Chrome 中就像一个魅力......我一直在为IOS和OSX的Safari中的问题而苦苦挣扎,几个月前,我在SO上找到了解决方法。
<body onunload="">
或通过 javascript
<script type="text/javascript">
window.onunload = function(e){
e.preventDefault();
return;
};
</script>
这有点丑陋,但可以工作一段时间。
我不知道为什么,但是将null返回到页面未在Safari中缓存的事件。onunload
我建议一种解决方法,将函数签名修改为如下所示:
getNewRecordID(intRecordType, strTimestamp) 然后始终传入 TimeStamp 参数,并在服务器端丢弃该值。这可以解决此问题。
我们发现,运行 iOS 版本 9 和 10 的旧款 iPhone 和 iPad 偶尔会返回虚假的空白 AJAX 结果,这可能是由于 Apple 降低了 CPU 速度。返回空白结果时,iOS 不会调用服务器,就像从缓存中返回结果一样。频率差异很大,大约有 10% 到 30% 的 AJAX 调用返回空白。
解决方案令人难以置信。只需等待 1 秒,然后再次拨打。在我们的测试中,只需要重复一次,但我们编写的代码最多可以调用 4 次。我们不确定是否需要 1 秒等待,但我们不想冒着因重复调用而给我们的服务器带来负担的风险。
我们发现问题发生在两个不同的 AJAX 调用上,调用具有不同数据的不同 API 文件。但我担心它可能发生在任何 AJAX 调用中。我们只是不知道,因为我们不会检查每个 AJAX 结果,也不会在旧设备上多次测试每个调用。
AJAX 调用使用的两个问题:POST, Asynchronously = true, setRequestHeader = ('Content-Type', 'application/x-www-form-urlencoded')
当问题发生时,通常只有一个 AJAX 调用在进行。因此,这不是由于重叠的 AJAX 调用。有时,当设备繁忙时会发生问题,但有时不会,如果没有 DevTools,我们真的不知道当时发生了什么。
iOS 13 不会这样做,Chrome 或 Firefox 也不会这样做。我们没有任何运行 iOS 11 或 12 的测试设备。也许其他人可以测试这些?
我在这里注意到这一点,因为在搜索这个问题时,这个问题是谷歌的顶级结果。
评论