Является ли Safari iOS 6 кэшированием $.ajax результатов?
С момента обновления до iOS 6 мы видим, что веб-просмотр Safari позволяет выполнять кеширование вызовов $.ajax
. Это в контексте приложения PhoneGap, поэтому оно использует Safari WebView. Наши вызовы $.ajax
- это методы POST
, и у нас установлен кеш в false {cache:false}
, но все же это происходит. Мы попытались вручную добавить TimeStamp
в заголовки, но это не помогло.
Мы провели больше исследований и обнаружили, что Safari возвращает только кэшированные результаты для веб-сервисов, у которых есть сигнатура функции, которая является статической и не изменяется от вызова к вызову. Например, представьте себе функцию, которая называется:
getNewRecordID(intRecordType)
Эта функция снова и снова вводит одни и те же параметры ввода, но возвращаемые данные должны быть разными каждый раз.
Должно быть, в спешке Apple, чтобы сделать iOS 6 zip впечатляюще, они слишком обрадовались настройкам кеша. Кто-нибудь еще видел это поведение на iOS 6? Если да, то что именно вызывает его?
Обходной путь, который мы обнаружили, заключался в том, чтобы изменить подпись функции таким образом:
getNewRecordID(intRecordType, strTimestamp)
а затем всегда передавать параметр TimeStamp
и просто отбрасывать это значение на стороне сервера. Это работает вокруг проблемы. Надеюсь, это поможет другой бедной душе, которая тратит 15 часов на этот вопрос, как я!
Ответы
Ответ 1
После небольшого исследования выяснилось, что Safari на iOS6 будет кэшировать POST, у которых нет ни заголовков Cache-Control, ни даже "Cache-Control: max-age = 0".
Единственный способ, с помощью которого предотвращение этого кэширования происходит на глобальном уровне, а не необходимость взлома случайных querystrings в конце вызовов службы, - установить "Cache-Control: no-cache".
Итак:
- Нет кеша-контроля или истекает заголовок = iOS6 Safari будет кэшировать
- Cache-Control max-age = 0 и немедленный Expires = iOS6 Safari будет кэшировать
- Cache-Control: no-cache = iOS6 Safari НЕ будет кэшировать
Я подозреваю, что Apple использует это из спецификации HTTP в разделе 9.5 о POST:
Ответы на этот метод не подлежат кэшированию, если только ответ включает соответствующие поля заголовка Cache-Control или Expires. Однако, ответ 303 (см. другой) можно использовать для направления пользовательского агента на получить ресурс, кэшируемый.
Таким образом, в теории вы можете кэшировать ответы POST... кто знал. Но ни один другой браузер не думал, что это будет хорошей идеей до сих пор. Но это НЕ учитывает кеширование, если не заданы заголовки Cache-Control или Expires, только когда есть какой-либо набор. Так что это должно быть ошибка.
Ниже я использую в правильной части моего Apache-конфигурации цель для всего моего API, потому что, поскольку это происходит, я фактически не хочу ничего кэшировать, даже получаю. Я не знаю, как установить это только для POST.
Header set Cache-Control "no-cache"
Обновление. Только что заметили, что я не указал, что это только когда POST будет одинаковым, поэтому измените любые данные POST или URL-адрес, и вы в порядке. Таким образом, вы можете, как упоминалось в другом месте, просто добавить некоторые случайные данные в URL-адрес или немного данных POST.
Обновление: вы можете ограничить "no-cache" только POST, если вы желаете это в Apache:
SetEnvIf Request_Method "POST" IS_POST
Header set Cache-Control "no-cache" env=IS_POST
Ответ 2
Я надеюсь, что это может пригодиться другим разработчикам, ударяющим головой о стену на этом. Я обнаружил, что любое из следующих действий предотвращает кэширование ответа POST Safari на iOS 6:
- добавление [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(), которые вы делаете, которые установлены в глобальные: false НЕ будет использовать параметры, указанные в $.ajaxSetup(), поэтому вам нужно будет добавить заголовки в еще раз.
Ответ 3
Простое решение для всех ваших запросов веб-сервисов, если вы используете 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()
}));
});
Подробнее о вызове prefilter jQuery здесь.
Если вы не используете jQuery, проверьте документы для выбранной вами библиотеки. Они могут иметь схожие функции.
Ответ 4
У меня была та же проблема с получением Webapp данных из веб-службы ASP.NET
Это сработало для меня:
public WebService()
{
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
...
}
Ответ 5
У меня была эта проблема также в приложении PhoneGap. Я решил это, используя функцию JavaScript getTime()
следующим образом:
var currentTime = new Date();
var n = currentTime.getTime();
postUrl = "http://www.example.com/test.php?nocache="+n;
$.post(postUrl, callbackFunction);
Я потратил несколько часов на то, чтобы понять это. Было бы неплохо, если Apple сообщит разработчикам об этой проблеме кеширования.
Ответ 6
Наконец, у меня есть решение проблемы с загрузкой.
В JavaScript:
var xhr = new XMLHttpRequest();
xhr.open("post", 'uploader.php', true);
xhr.setRequestHeader("pragma", "no-cache");
В PHP:
header('cache-control: no-cache');
Ответ 7
Из моего собственного сообщения в блоге 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");
Обязательно добавьте это вверху страницы, прежде чем какие-либо данные будут отправлены клиенту.
.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.
Ответ 8
Этот фрагмент кода отлично работает с jQuery и jQuery Mobile:
$.ajaxSetup({
cache: false,
headers: {
'Cache-Control': 'no-cache'
}
});
Просто поместите его где-нибудь в свой код JavaScript (после загрузки jQuery и лучше всего перед выполнением AJAX-запросов), и это должно помочь.
Ответ 9
Вы также можете исправить эту проблему, изменив jQuery Ajax, выполнив следующее (начиная с 1.7.1) в верхней части функции Ajax (функция начинается с строки 7212). Это изменение активирует функцию встроенного анти-кэша jQuery для всех запросов POST.
(Полный script доступен в 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 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 : "");
}
}
To:
// 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 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 : "");
}
Ответ 10
Быстрая работа для служб GWT-RPC заключается в том, чтобы добавить это ко всем удаленным методам:
getThreadLocalResponse().setHeader("Cache-Control", "no-cache");
Ответ 11
Это обновление ответа 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();
}
});
Ответ 12
Чтобы решить эту проблему для WebApps, добавленных на главный экран, необходимо соблюдать обе изложенные выше обходные пути. Кэширование необходимо отключить на веб-сервере, чтобы предотвратить повторное кэширование новых запросов, и некоторые случайные вводные данные необходимо добавлять к каждому запросу для отправки запросов, которые уже были кэшированы для прохождения. Пожалуйста, обратитесь к моему сообщению:
iOS6 - Есть ли способ очистить кэшированные запросы POST ajax для добавления webapp на главный экран?
ПРЕДУПРЕЖДЕНИЕ: всем, кто реализовал обходной путь, добавив временную метку к своим запросам, не отключая кеширование на сервере. Если ваше приложение добавляется на главный экран, КАЖДЫЙ пост-ответ теперь будет кэшироваться, очистка кеша сафари не очистит его, и он, похоже, не истечет. Если у кого-то есть способ его очистить, это похоже на потенциальную утечку памяти!
Ответ 13
Что работает для 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);
Ответ 14
Вещи, которые НЕ РАБОТАЮТ для меня с iPad 4/iOS 6:
Мой запрос, содержащий: Cache-Control: no-cache
//asp.net's:
HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache)
Добавление кеша: 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);
Ответ 15
Мое обходное решение в ASP.NET (pagemethods, webservice и т.д.)
protected void Application_BeginRequest(object sender, EventArgs e)
{
Response.Cache.SetCacheability(HttpCacheability.NoCache);
}
Ответ 16
При добавлении параметров кэша-бастера, чтобы сделать внешний вид запроса похожим на твердое решение, я бы посоветовал это сделать, так как это повредило бы любое приложение, основанное на фактическом кэшировании. Создание API-интерфейсов правильными заголовками является наилучшим решением, даже если это немного сложнее, чем добавление кэш-памяти для вызывающих.
Ответ 17
Для тех, кто использует 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() {
}
}
Ответ 18
Я смог исправить свою проблему, используя комбинацию $.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');
}
}
});
});
});
Ответ 19
Я думаю, что вы уже решили свою проблему, но позвольте мне поделиться идеей о веб-кешировании.
Правда, вы можете добавить много заголовков на каждом используемом вами языке, на стороне сервера, на стороне клиента, и вы можете использовать многие другие трюки, чтобы избежать кеширования в Интернете, но всегда думайте, что вы никогда не сможете узнать, откуда клиент подключается к ваш сервер, вы никогда не знаете, использует ли он соединение "Hot-Spot" отеля, которое использует Squid или другие продукты кеширования.
Если пользователи используют прокси-сервер, чтобы скрыть свою реальную позицию и т.д. реальный единственный способ избежать кеширования - это отметка времени в запросе и если она не используется.
Например:
/ajax_helper.php?ts=3211321456
Затем каждый менеджер кэша, который вы должны передать, не нашел тот же URL-адрес в репозитории кеша и перегрузил содержимое страницы.
Ответ 20
Я предлагаю обходное решение для модификации сигнатуры функции как нечто подобное:
getNewRecordID (intRecordType, strTimestamp)
а затем всегда передавать параметр TimeStamp и просто отбрасывать это значение на стороне сервера. Это работает вокруг проблемы.
Ответ 21
В зависимости от приложения вы можете устранить проблему сейчас в iOS 6 с помощью Safari > Advanced > Web Inspector, так что это поможет в этой ситуации.
Подключите телефон к Safari на Mac, а затем используйте меню разработчика, чтобы устранить проблему с веб-приложением.
Очистите данные веб-сайта на iPhone после обновления до iOS6, в том числе и для приложения, используя веб-представление. Только одно приложение имело проблему, и это разрешило его во время тестирования бета-тестирования IOS6, с тех пор никаких реальных проблем.
Вам может потребоваться также посмотреть ваше приложение, проверьте NSURLCache, если в WebView в пользовательском приложении.
https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSURLCache_Class/Reference/Reference.html#//apple_ref/doc/uid/TP40003754
Я думаю, в зависимости от истинной природы вашей проблемы, реализации и т.д.
Ссылка: $.ajax calls
Ответ 22
Я нашел одно обходное решение, из-за которого мне стало интересно, почему он работает. Прежде чем прочитать ответ Tadej о веб-сервисе ASP.NET, я пытался придумать что-то, что сработает.
И я не говорю, что это хорошее решение, но я просто хотел зарегистрировать его здесь.
главная страница: включает функцию 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 с дочерней страницы, я получаю обновленный контент.
Ответ 23
Пока мои страницы входа и регистрации работают как очарование в Firefox, IE и Chrome... Я боролся с этой проблемой в Safari для IOS и OSX, несколько месяцев назад я нашел обходное решение для SO.
<body onunload="">
ИЛИ через javascript
<script type="text/javascript">
window.onunload = function(e){
e.preventDefault();
return;
};
</script>
Это своеобразная уродливая вещь, но работает какое-то время.
Я не знаю почему, но возвращая значение null в событие onunload
, страница не кэшируется в Safari.
Ответ 24
В Ruby Sinatra
before '*' do
if env['REQUEST_METHOD'] == 'POST'
headers 'Cache-Control' => 'no-cache, no-store, must-revalidate'
end
end
Ответ 25
Он работал с ASP.NET только после добавления заголовка pragma:no-cache
в IIS. Cache-Control: no-cache
было недостаточно.