自从升级到 iOS 6 后,我们看到 Safari 的 Web 视图可以随意缓存 $.ajax
调用。这是在 PhoneGap 应用程序的上下文中,因此它使用的是 Safari WebView。我们的 $.ajax
调用是 POST
方法,我们将缓存设置为 false {cache:false}
,但这种情况仍在发生。我们尝试手动将 TimeStamp
添加到标题中,但没有帮助。
我们进行了更多研究,发现 Safari 仅返回具有静态函数签名且不会随调用而变化的 Web 服务的缓存结果。例如,想象一个名为的函数:
getNewRecordID(intRecordType)
这个函数一遍又一遍地接收相同的输入参数,但它每次返回的数据应该是不同的。
一定是 Apple 急于让 iOS 6 快速运行,他们对缓存设置太满意了。有没有其他人在 iOS 6 上看到过这种行为?如果是这样,究竟是什么原因造成的?
我们找到的解决方法是将函数签名修改为如下所示:
getNewRecordID(intRecordType, strTimestamp)
然后总是传递一个 TimeStamp
参数,并在服务器端丢弃该值。这可以解决这个问题。
经过一番调查,事实证明 iOS6 上的 Safari 将缓存没有 Cache-Control 标头甚至“Cache-Control: max-age=0”的 POST。
我发现防止这种缓存在全局级别发生而不是不得不将随机查询字符串破解到服务调用结束的唯一方法是设置“Cache-Control:no-cache”。
所以:
没有 Cache-Control 或 Expires 标头 = 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 就可以了。因此,您可以像其他地方提到的那样,向 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 预过滤器调用 here 的更多信息。
如果您不使用 jQuery,请查看您选择的库的文档。它们可能具有类似的功能。
我刚刚在 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()
的选项,但根据 the docs,这些参数被忽略; jQuery 将“从不缓存”发布请求,但不考虑浏览器。也许更简洁的选择是使用 $.ajaxPrefilter()
为请求添加时间戳。
function send_ajax(my_data,refresh)
.. 参考这里stackoverflow.com/questions/14733772/…
我从 ASP.NET webservice 获取数据的 webapp 遇到了同样的问题
这对我有用:
public WebService()
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
...
}
最后,我有一个解决上传问题的方法。
在 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 caching Ajax POST requests:
如何解决:有多种方法可以防止请求缓存。推荐的方法是添加一个无缓存标头。这就是它的完成方式。
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) {…}
服务器端
爪哇:
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 请求之前),它应该会有所帮助。
您还可以通过修改 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
,它允许您在发出之前修改您的 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();
}
});
为了解决添加到主屏幕的 WebApp 的此问题,需要遵循两个投票最多的解决方法。需要在网络服务器上关闭缓存以防止新请求被缓存,并且需要向每个发布请求添加一些随机输入,以便已经缓存的请求能够通过。请参考我的帖子:
iOS6 - Is there a way to clear cached ajax POST requests for webapp added to home screen?
警告:对于通过在请求中添加时间戳而不关闭服务器上的缓存来实现解决方法的任何人。如果您的应用程序被添加到主屏幕,现在将缓存每个发布响应,清除 safari 缓存不会清除它并且它似乎不会过期。除非有人有办法清除它,否则这看起来像是潜在的内存泄漏!
iPad 4/iOS 6 不适合我的事情:
我的请求包含: Cache-Control:no-cache
//asp.net's:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache)
在我的 jQuery ajax 调用中添加 cache: false
$.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()
附加 url,因此不再需要手动附加时间戳。
这就是 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 中的解决方法(页面方法、网络服务等)
protected void Application_BeginRequest(object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
}
虽然添加缓存破坏器参数以使请求看起来不同似乎是一个可靠的解决方案,但我建议不要这样做,因为它会损害任何依赖于实际缓存发生的应用程序。使 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 并将时间戳附加到我的帖子的网址(而不是帖子参数/正文)来解决我的问题。这是基于先前答案的建议
$(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');
}
}
});
});
});
我想你已经解决了你的问题,但让我分享一个关于网络缓存的想法。
确实,您可以在您使用的每种语言(服务器端、客户端)中添加许多标头,并且您可以使用许多其他技巧来避免 Web 缓存,但始终认为您永远无法知道客户端从哪里连接到您的服务器,你永远不知道他是否使用了使用 Squid 或其他缓存产品的酒店“热点”连接。
如果用户正在使用代理来隐藏他的真实位置等……避免缓存的唯一方法是请求中的时间戳,如果未使用的话。
例如:
/ajax_helper.php?ts=3211321456
然后,您必须传递的每个缓存管理器都没有在缓存存储库中找到相同的 URL,然后重新下载页面内容。
$.ajax
并将选项设置为 {cache:false}
,那么 jQuery 本身就会自动在幕后添加缓存清除,而您无需执行任何其他操作。
根据应用程序,您现在可以在 iOS 6 中使用 Safari>Advanced>Web Inspector 解决问题,这有助于解决这种情况。
将手机连接到 Mac 上的 Safari,然后使用开发人员菜单对 Web 应用程序进行故障排除。
更新到 iOS6 后清除 iPhone 上的网站数据,包括特定于使用 Web 视图的应用程序。只有一个应用程序有问题,这在 IOS6 Beta 测试期间解决了它,从那时起没有真正的问题。
您可能还需要查看您的应用程序,如果在自定义应用程序的 WebView 中,请查看 NSURLCache。
我想这取决于您的问题、实施等的真实性质。..
参考:$.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 中缓存。
我们发现运行 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 的测试设备。也许其他人可以测试这些设备?
我在这里注意到这一点,因为在搜索此问题时,此问题是 Google 搜索结果中排名靠前的。
我建议一种解决方法来修改函数签名,如下所示:
getNewRecordID(intRecordType, strTimestamp) 然后总是传入一个 TimeStamp 参数,并在服务器端丢弃该值。这可以解决这个问题。
不定期副业成功案例分享