Czy Safari na iOS 6 buforuje wyniki $ .ajax?

1072

Od czasu uaktualnienia do systemu iOS 6 widzimy, że widok przeglądarki Safari pozwala na buforowanie $.ajaxpołączeń. Jest to w kontekście aplikacji PhoneGap, więc korzysta ona z Safari WebView. Nasze $.ajaxwywołania są POSTmetodami, a pamięć podręczna ma wartość false {cache:false}, ale tak się dzieje. Próbowaliśmy ręcznie dodać a TimeStampdo nagłówków, ale to nie pomogło.

Zrobiliśmy więcej badań i stwierdziliśmy, że Safari zwraca wyniki z pamięci podręcznej tylko dla usług internetowych, które mają podpis funkcji, który jest statyczny i nie zmienia się z połączenia do połączenia. Na przykład wyobraź sobie funkcję o nazwie coś takiego:

getNewRecordID(intRecordType)

Ta funkcja odbiera ciągle te same parametry wejściowe, ale zwracane dane powinny za każdym razem być inne.

Musi być w pośpiechu Apple, aby imponujący zip w iOS 6 był zbyt zadowolony z ustawień pamięci podręcznej. Czy ktoś jeszcze widział takie zachowanie na iOS 6? Jeśli tak, co dokładnie to powoduje?


Obejściem tego problemu było zmodyfikowanie podpisu funkcji w taki sposób:

getNewRecordID(intRecordType, strTimestamp)

a następnie zawsze przekazuj również TimeStampparametr i po prostu odrzuć tę wartość po stronie serwera. Działa to wokół problemu. Mam nadzieję, że pomoże to innej biednej duszy, która spędza 15 godzin w tej sprawie, tak jak ja!

użytkownik1684978
źródło
190
To jest absolutnie szokujące. Właśnie spędziliśmy kilka godzin, próbując ustalić, co właśnie przestało działać. Nasz login AJAX, który wykonuje POST (i ma również nagłówki, aby zapobiec buforowaniu), jest buforowany przez Safari, więc po prostu zwraca ten sam JSON, co ostatnio, nawet nie próbując serwera ... niewiarygodne! Będziemy musieli zhakować poprawkę, ale nigdy nie powinieneś buforować POST, to szalone.
Kieran
16
Opublikuj swoje rozwiązanie jako odpowiedź, a nie jako aktualizację pytania.
ChrisF
50
Żądania POST nie są idempotentne, co oznacza, że ​​nie powinny być buforowane, chyba że odpowiedź wyraźnie zaleci to poprzez nagłówki odpowiedzi.
James M. Greene,
6
Aby Apple to naprawić, zgłoś błąd na bugreport.apple.com . Zrobiłem to samo.
Mathias Bynens,
11
Mark Nottingham (przewodniczący grupy roboczej IETF HTTPbis) napisał dzisiaj ciekawy wpis na blogu na ten temat: mnot.net/blog/2012/09/24/caching_POST
Benjamin Brizzi

Odpowiedzi:

447

Po krótkiej analizie okazuje się, że Safari na iOS6 będzie buforować pliki POST, które nie mają nagłówków Cache-Control, a nawet „Cache-Control: max-age = 0”.

Jedynym sposobem, w jaki udało mi się zapobiec temu buforowaniu na poziomie globalnym, zamiast konieczności włamywania się do losowych zapytań na koniec zgłoszeń serwisowych, jest ustawienie „Kontrola pamięci podręcznej: brak pamięci podręcznej”.

Więc:

  • Brak kontroli Cache lub wygasa nagłówki = iOS6 Safari będzie buforować
  • Cache-Control max-age = 0 i natychmiastowe wygaśnięcie = iOS6 Safari będzie buforować
  • Kontrola pamięci podręcznej: no-cache = iOS6 Safari NIE będzie buforować

Podejrzewam, że Apple korzysta z tego na podstawie specyfikacji HTTP w sekcji 9.5 na temat POST:

Odpowiedzi na tę metodę nie są buforowane, chyba że odpowiedź zawiera odpowiednie pola nagłówka Cache-Control lub Expires. Jednak odpowiedź 303 (Zobacz inne) może być użyta do nakierowania agenta użytkownika na pobranie zasobu buforowanego.

Teoretycznie możesz buforować odpowiedzi POST ... kto wiedział. Ale jak dotąd żaden inny twórca przeglądarki nie pomyślał, że to dobry pomysł. Ale to NIE uwzględnia buforowania, gdy nie są ustawione nagłówki Kontroli pamięci podręcznej lub Wygasają, tylko jeśli są ustawione. To musi być błąd.

Poniżej znajduje się to, czego używam w odpowiednim fragmencie mojej konfiguracji Apache do kierowania na cały mój API, ponieważ tak się składa, że ​​tak naprawdę nie chcę niczego buforować, nawet dostaje. Nie wiem, jak ustawić to tylko dla testów POST.

Header set Cache-Control "no-cache"

Aktualizacja: Właśnie zauważyłem, że nie zauważyłem, że dzieje się tak tylko wtedy, gdy POST jest taki sam, więc zmień dowolne dane lub adres URL POST i nic ci nie będzie. Możesz więc, jak wspomniano w innym miejscu, po prostu dodać losowe dane do adresu URL lub trochę danych POST.

Aktualizacja: Możesz ograniczyć „brak pamięci podręcznej” tylko do POST, jeśli chcesz tak w Apache:

SetEnvIf Request_Method "POST" IS_POST
Header set Cache-Control "no-cache" env=IS_POST
Kieran
źródło
7
Widzę, dokąd zmierza Apple, ale widzimy buforowane odpowiedzi na żądania POST, nawet jeśli nasze odpowiedzi nie zawierały nagłówków Cache-Control lub Expires. Czy to wystąpienie iOS6 nie powinien buforować i wysyłać każdego żądania. To się nie dzieje.
Kango_V
138
Część specyfikacji HTTP, którą zacytowałeś, nie uzasadnia zachowania pamięci podręcznej iOS 6. Domyślnym zachowaniem powinno być nie buforowanie odpowiedzi POST (tj. Gdy nagłówek „Kontrola pamięci podręcznej” nie jest zdefiniowany). To zachowanie narusza specyfikację i powinno być traktowane jako błąd. Każdy, kto buduje usługi sieciowe xml / json api, powinien ozdobić swoje odpowiedzi POST komunikatem „Cache-control: no-cache”, aby obejść ten problem.
David H
39
Żądania POST nie są idempotentne, co oznacza, że ​​nie powinny być buforowane, chyba że odpowiedź wyraźnie zaleci to poprzez nagłówki odpowiedzi.
James M. Greene,
4
Jak mówi David, jest to wyraźne naruszenie cytowanego zdania. Jeśli nie ma pól „Kontrola pamięci podręcznej lub pola nagłówka wygasają”, odpowiednie takie nagłówki oczywiście nie są uwzględniane. Jednak twoje własne dochodzenie pokazuje, że w tym scenariuszu są buforowane. Edytuj swoją odpowiedź.
Matthew Flaschen
3
Czy ktoś wie, jak długo wynik jest buforowany na urządzeniu? Próbowałem zabić safari i ponownie uruchomić telefon, ale nadal jest on w pamięci podręcznej. Wiem, że to działa z czyszczeniem pamięci podręcznej przeglądarki, ale zastanawiam się, ile czasu zajmie użytkownikom, którzy kiedyś mieli problem, zanim zniknie. Nie wszyscy pomyślą o wyczyszczeniu pamięci podręcznej ...
Daniel Hallqvist,
146

Mam nadzieję, że może to być przydatne dla innych programistów walących głową o ścianę. Odkryłem, że którykolwiek z poniższych przypadków uniemożliwia przeglądarce Safari w systemie iOS 6 buforowanie odpowiedzi POST:

  • dodawanie [kontrola pamięci podręcznej: brak pamięci podręcznej] w nagłówkach żądania
  • dodając zmienny parametr URL, taki jak aktualny czas
  • dodanie [pragma: no-cache] w nagłówkach odpowiedzi
  • dodawanie [cache-control: no-cache] w nagłówkach odpowiedzi

Moje rozwiązanie było następujące w moim Javascript (wszystkie moje żądania AJAX to POST).

$.ajaxSetup({
    type: 'POST',
    headers: { "cache-control": "no-cache" }
});

Dodam również nagłówek [pragma: no-cache] do wielu moich odpowiedzi serwera.

Jeśli użyjesz powyższego rozwiązania, pamiętaj, że wszelkie wywołania $ .ajax (), które są ustawione na globalne: false NIE będą używać ustawień określonych w $ .ajaxSetup (), więc będziesz musiał ponownie dodać nagłówki.

Dave
źródło
4
To jest PRAWIDŁOWE rozwiązanie błędu. Błąd polega na tym, że iOS 6 będzie obsługiwał żądania POST ze swojej pamięci podręcznej zamiast wysyłać je na serwer. Błąd nie polega na tym, że buforuje odpowiedzi z żądań POST (co jest dozwolone). Jeśli nadal chcesz otrzymywać odpowiedzi na żądania POST pobrane z pamięci podręcznej na kolejne żądania GET do tego identyfikatora URI, skorzystaj z tego rozwiązania.
Nicholas Shanks,
2
To działa dla mnie, ale nie rozumiem jak. Podałem już cache: false w moim ajaxSetup i patrząc na nagłówki żądania, które sprowadzają się do Cache-Control: no-cache i Pragma: no-cache - ale nadal będzie buforować na iPadzie. Następnie, gdy dodam nagłówki: {"cache-control": "no-cache"} do ajaxSetup, podwaja nagłówek Cache-Control, aby był „no-cache, no-cache” - i zatrzymuje buforowanie. Co tu się dzieje?
Tom W Hall
Działa idealnie - możesz również dodać do żądania jako parametr $ .ajax ({type: 'POST', nagłówki: {'cache-control': 'no-cache'} itd.})
George Filippakos
Co to jest [pragma: no-cache]? Do czego służy klucz pragma?
zakdances
Myślę też, że jest to najlepsze podejście, a nie obejście z dodatkowym parametrem. Dodaliśmy to tylko do połączeń tam, gdzie były potrzebne, dla połączeń, które zawsze mają ten sam zwrot, buforowanie jest prawdopodobnie dobrą rzeczą dla użytkownika końcowego.
germankiwi
67

Proste rozwiązanie dla wszystkich żądań usług sieciowych, przy założeniu, że używasz 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()
    }));
});

Przeczytaj więcej o wywołaniu filtra wstępnego jQuery tutaj .

Jeśli nie korzystasz z jQuery, sprawdź dokumenty w wybranej bibliotece. Mogą mieć podobną funkcjonalność.

Baz1nga
źródło
3
Dla mnie to nie działa, serwer odpowiada: „Nieprawidłowy prymitywny JSON: timeStamp” asp.net / iis 7.5
Alexandre
3
co z $ .ajax ({"cache": false ...})? będzie działać, gdy doda _ _ = [TIMESTAMP]? (Nie posiadam takiego urządzenia do testowania go)
Karussell
Zamieściłem pełną implementację rozwiązania zaproponowanego przez Karussell. Zobacz moją odpowiedź poniżej.
Sam Shiles,
1
@Karussell. Właśnie próbowałem ustawić $ .ajax ({"cache": false ...}). To nie rozwiązuje problemu z żądaniami POST na iOS6. Przypuszczalnie dlatego, że JQuery zgodnie z ich dokumentami zakłada, że ​​żadna przeglądarka nie jest na tyle głupia, aby buforować żądania postów. „Strony pobrane za pomocą POST nigdy nie są buforowane, więc opcje cache i ifModified w jQuery.ajaxSetup () nie mają wpływu na te żądania.”
Brett Hannah
1
To nie działa Nie łączy parametrów postu. Post autorstwa Dave'a jest lepszym rozwiązaniem.
Chris Muench
43

Właśnie miałem ten problem w aplikacji PhoneGap . Rozwiązałem go za pomocą funkcji JavaScript getTime()w następujący sposób:

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

Zmarnowałem kilka godzin, zastanawiając się nad tym. Byłoby miło, gdyby Apple powiadomił programistów o tym problemie z buforowaniem.

Bashevis
źródło
1
Zamierzałem skomentować użycie {cache:false}jako opcji albo, $.post()albo $.ajaxSetup(), zgodnie z dokumentami , te argumenty są ignorowane; jQuery „nigdy nie buforuje” żądań wysyłania, ale nie bierze pod uwagę przeglądarki. Być może lepszym rozwiązaniem byłoby dodanie znacznika czasu do żądań za pomocą $.ajaxPrefilter().
fwielstra
spędzam prawie 5 godzin, aby naprawić ten problem, a na końcu dodanie znacznika czasu załatwi sprawę function send_ajax(my_data,refresh) . patrz tutaj stackoverflow.com/questions/14733772/...
rusly
42

Miałem ten sam problem z aplikacją internetową pobierającą dane z usługi ASP.NET

To działało dla mnie:

public WebService()
{
    HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    ...
}
Tadej
źródło
2
Dziękuję Ci bardzo! Oszalałem, próbując dowiedzieć się, dlaczego iPhone zachowuje się tak inaczej niż każda inna platforma. To rozwiązanie specyficzne dla ASP.NET pozwoliło mi zaoszczędzić mnóstwo czasu.
Mark Brittingham,
Nie działał na iOS6, zobacz moją odpowiedź pod koniec wątku
Brian Ogden
1
Proszę!!!! Postaw warunek, aby zastosować to tylko na IOS 6, pamięć podręczna zawartości jest niezbędna dla każdej aplikacji.
Alexandre,
24

Wreszcie mam rozwiązanie mojego problemu z przesyłaniem.

W JavaScript:

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

W PHP :

header('cache-control: no-cache');
goker.cebeci
źródło
15

Z mojego własnego posta na blogu iOS 6.0 buforuje żądania Ajax POST :

Jak to naprawić: Istnieją różne metody zapobiegania buforowaniu żądań. Zalecaną metodą jest dodanie nagłówka bez pamięci podręcznej. Tak to się robi.

jQuery:

Sprawdź iOS 6.0 i ustaw nagłówek Ajax w następujący sposób:

$.ajaxSetup({ cache: false });

ZeptoJS:

Sprawdź system iOS 6.0 i ustaw nagłówek Ajax w następujący sposób:

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

Po stronie serwera

Jawa:

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

Pamiętaj, aby dodać to u góry strony, zanim jakiekolwiek dane zostaną wysłane do klienta.

.NETTO

Response.Cache.SetNoStore();

Lub

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.
kiranvj
źródło
2
Dobry atrybut bez pamięci podręcznej dla platformy .NET stackoverflow.com/questions/10011780/…
Aran Mulholland
7

Ten fragment kodu JavaScript działa doskonale w przypadku jQuery i jQuery Mobile:

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

Po prostu umieść go gdzieś w kodzie JavaScript (po załadowaniu jQuery, najlepiej przed zrobieniem żądań AJAX) i powinno to pomóc.

Jonathan
źródło
6

Możesz również rozwiązać ten problem, modyfikując funkcję Ajax jQuery , wykonując następujące czynności (od 1.7.1) na górze funkcji Ajax (funkcja zaczyna się od linii 7212). Ta zmiana aktywuje wbudowaną funkcję anty-cache jQuery dla wszystkich żądań POST.

(Pełny skrypt jest dostępny pod adresem http://dl.dropbox.com/u/58016866/jquery-1.7.1.js.)

Wstaw poniżej wiersza 7221:

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

Następnie zmodyfikuj następujące elementy (od linii ~ 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 : "");
    }
}

Do:

// 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 : "");
}
Sam Shiles
źródło
4
Nie jest dobrym podejściem do zmiany jQuery lub jeśli chodzi o dowolny kod, którego nie posiadasz. (Za każdym razem, gdy chcesz zaktualizować wersję, musisz wprowadzić ją ponownie. (Lub Kolejne aktualizacje dla programistów, a program nie działa))
andlrc
Jest to całkowicie poprawne podejście, jeśli potrzebujesz najszybszego możliwego rozwiązania, aby złagodzić idiotyzm Apple. To rozwiązanie zostało zastosowane do rozwiązania problemu ogromnej witryny, która otrzymuje miliony odwiedzin dziennie i umożliwiła nam to po prostu poprzez zmianę jednego pliku.
Sam Shiles,
Możesz na to spojrzeć jQuery.ajaxPrefiler, aby zmodyfikować zapytanie ajax bezpośrednio przed jego wykonaniem. Możesz zarchiwizować to samo z bardziej zoptymalizowanym i zaktualizować bezpieczny kod.
andlrc
1
Problem z podejściem do filtra wstępnego polega na tym, że musisz zarejestrować filtr. Jeśli masz wspólny skrypt uruchamiany podczas ładowania każdej strony, to dobrze, ale jeśli nie, musisz ustawić filtr wstępny dla każdej strony korzystającej z ajax. W scenariuszu, z którym miałem do czynienia, mieliśmy wspólną lokalizację pliku JQ, który był używany jako zasób dla 7+ pojedynczych stron internetowych. Z powodu tego błędu traciliśmy tysiące funtów na godzinę, a podejście, które zasugerowałem, pozwoliło nam go rozwiązać w możliwie najkrótszym czasie, zmieniając JEDEN plik. Zgadzam się z tobą zasadniczo, ale czasami musisz być pragmatyczny!
Sam Shiles,
Następnie możesz ponownie dodać go na końcu tego pliku. Dobrze, że to rozwiązałeś, twoja firma musi być z ciebie szczęśliwa.
andlrc
5

Szybkim obejściem usług GWT-RPC jest dodanie tego do wszystkich metod zdalnych:

getThreadLocalResponse().setHeader("Cache-Control", "no-cache");
Lars Høidahl
źródło
Większość z nas ma setki zdalnych metod w swoich wdrożeniach GWT. Czy istnieje uniwersalny sposób ustawiania nagłówka kontroli pamięci podręcznej dla wszystkich żądań?
dirkoneill
5

To jest aktualizacja odpowiedzi Baz1nga. Ponieważ options.datanie jest obiektem, ale ciągiem, po prostu uciekłem się do konkatenacji znacznika czasu:

$.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();
  }
});
remcoder
źródło
1
Dodawanie znaczników czasu to zły pomysł, zamiast tego wypróbuj rozwiązanie Dave'a.
Nicholas Shanks,
4

Aby rozwiązać ten problem dla aplikacji internetowych dodanych do ekranu głównego, należy przestrzegać obu najczęściej głosowanych obejść. Buforowanie musi być wyłączone na serwerze WWW, aby zapobiec buforowaniu nowych żądań w przyszłości, a do każdego żądania postu należy dodać losowe dane wejściowe, aby żądania, które już zostały buforowane, zostały zrealizowane. Proszę odnieść się do mojego postu:

iOS6 - Czy istnieje sposób na wyczyszczenie buforowanych żądań POST dla aplikacji WWW dodanych do ekranu głównego?

OSTRZEŻENIE: każdemu, kto zaimplementował obejście, dodając znacznik czasu do swoich żądań bez wyłączania buforowania na serwerze. Jeśli Twoja aplikacja zostanie dodana do ekranu głównego, KAŻDY post postu będzie teraz buforowany, wyczyszczenie pamięci podręcznej safari nie wyczyści jej i nie będzie wygasać. Chyba że ktoś ma sposób, aby to wyczyścić, wygląda to na potencjalny wyciek pamięci!

fbader
źródło
Czy wszystkie odpowiedzi zostaną zapisane w pamięci podręcznej pliku lub pamięci w telefonie?
Eydun
Tak nie było ze mną. Dodałem znacznik czasu do mojego adresu URL (nie parametrów publikowania) i działa dobrze, zarówno podczas przeglądania z safari, jak i podczas zapisywania na ekranie głównym.
ShadeTreeDeveloper
4

Rzeczy, które NIE DZIAŁAŁY dla mnie na iPadzie 4 / iOS 6:

Moja prośba zawiera: Cache-Control: no-cache

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

Dodanie bufora: false do mojego wywołania jQuery ajax

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

Tylko to załatwiło sprawę:

var currentTime = new Date();
var n = currentTime.getTime();
postUrl = "http://www.example.com/test.php?nocache="+n;
$.post(postUrl, callbackFunction);
Brian Ogden
źródło
Za co głosowano w dół? Jest to ważna pamięć podręczna informacji: false nie działa z iPad4 / iOS6 ani //asp.net: HttpContext.Current.Response.Cache.SetCacheability (HttpCacheability.NoCache)
Brian Ogden
W przypadku potomności: od 2017 r. $.ajax cache: falseDołącza adres URL do parametru zapytania _=Date.prototype.getTime(), więc ręczne dołączanie znacznika czasu nie powinno być dłużej potrzebne.
cowbert
3

To jest obejście 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);    
Spiff
źródło
2

Moje obejście w ASP.NET (pagemethods, webservice itp.)

protected void Application_BeginRequest(object sender, EventArgs e)
{
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
}
Alexandre
źródło
1

Chociaż dodanie parametrów pomijania pamięci podręcznej, aby wyglądało inaczej, wydaje się solidnym rozwiązaniem, odradzam to, ponieważ zaszkodziłoby każdej aplikacji, która opiera się na faktycznym buforowaniu. Uczynienie wyjściowymi interfejsami API poprawnych nagłówków jest najlepszym możliwym rozwiązaniem, nawet jeśli jest to nieco trudniejsze niż dodawanie modułów blokujących pamięć podręczną do dzwoniących.

Ivo Jansch
źródło
1
Chociaż zgadzam się z tobą w większości przypadków, twierdzę, że prawdziwym rozwiązaniem tego problemu jest prawidłowe wdrożenie protokołu HTTP przez Apple. Mając to na uwadze, do tej pory nie obwiniałbym wielu programistów za wdrożenie najprostszego możliwego rozwiązania. Dla mnie modyfikacja implementacji jquery była najprostszą poprawką, ponieważ pozwoliła mi dokonać jednej edycji i mieć pewność, że była aktywna dla całej mojej witryny.
Sam Shiles,
1

Dla tych, którzy używają Struts 1, oto jak naprawiłem problem.

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() {
    }

}
cbmeeks
źródło
1

Byłem w stanie rozwiązać problem, używając kombinacji $ .ajaxSetup i dołączając znacznik czasu do adresu URL mojego postu (nie do parametrów / treści postu). Opiera się to na zaleceniach poprzednich odpowiedzi

$(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');
                }
            }
        });
    });
});
ShadeTreeDeveloper
źródło
1

Myślę, że rozwiązałeś już swój problem, ale pozwól, że podzielę się pomysłami na temat buforowania w sieci.

To prawda, że ​​możesz dodać wiele nagłówków w każdym używanym języku, po stronie serwera, po stronie klienta i możesz użyć wielu innych sztuczek, aby uniknąć buforowania w sieci, ale zawsze myśl, że nigdy nie wiesz, skąd klient łączy się z twoim serwerem, nigdy nie wiadomo, czy korzysta on z hotelowego połączenia „Hot-Spot”, które wykorzystuje Squid lub inne produkty buforowania.

Jeśli użytkownicy używają proxy do ukrycia swojej prawdziwej pozycji itp.… Prawdziwej jedynym sposobem uniknięcia buforowania jest znacznik czasu w żądaniu, również jeśli nie jest używany.

Na przykład:

/ajax_helper.php?ts=3211321456

Następnie każdy menedżer pamięci podręcznej, który musisz przekazać, nie znalazł tego samego adresu URL w repozytorium pamięci podręcznej i ponownie pobierał zawartość strony.

Lanello
źródło
Stara odpowiedź, ale moje dwa centy: jest to ogólnie dobra rada i zrozumiała dla większości kompetentnych programistów, ale w konkretnym przypadku jQuery, jeśli utworzysz $.ajaxi ustawisz opcje, aby {cache:false}jQuery automatycznie dodało pomijanie pamięci podręcznej za kulisami, bez potrzeby robienia czegokolwiek innego.
JakeGould
0

W zależności od aplikacji możesz teraz rozwiązać problem w iOS 6 za pomocą Safari> Zaawansowane> Inspektor sieci, więc jest to pomocne w tej sytuacji.

Podłącz telefon do Safari na komputerze Mac, a następnie użyj menu programisty, aby rozwiązać problemy z aplikacją internetową.

Wyczyść dane witryny na iPhonie po aktualizacji do iOS6, w tym dane specyficzne dla aplikacji za pomocą Widoku internetowego. Tylko jedna aplikacja miała problem i to rozwiązało go podczas testów IOS6 Beta w przeszłości, od tamtej pory nie było żadnych poważnych problemów.

Konieczne może być również spojrzenie na aplikację, sprawdź NSURLCache, jeśli w WebView w aplikacji niestandardowej.

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

Myślę, że w zależności od prawdziwej natury twojego problemu, wdrożenia itp.

Ref: $ .ajax połączenia

Steven Strauss
źródło
Chociaż nie odnosi się to bezpośrednio do pierwotnego pytania, jest to bardzo przydatna informacja pozwalająca na ogólne rozwiązywanie problemów na urządzeniach, więc głosuję za nią.
Kris Giesing,
0

Znalazłem jedno obejście, które mnie ciekawi, dlaczego to działa. Zanim przeczytałem odpowiedź Tadej'a dotyczącą serwisu internetowego ASP.NET, próbowałem wymyślić coś, co by działało.

I nie mówię, że to dobre rozwiązanie, ale chciałem to tutaj udokumentować.

strona główna: zawiera funkcję JavaScript, checkStatus (). Metoda wywołuje inną metodę, która używa wywołania AJAX jQuery do aktualizacji zawartości HTML. Użyłem setInterval do wywołania checkStatus (). Oczywiście napotkałem problem z buforowaniem.

Rozwiązanie: użyj innej strony, aby wywołać aktualizację.

Na stronie głównej ustawiłem zmienną boolowską, runUpdate, i dodałem następujące do tagu body:

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

W pliku helper.html:

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

Jeśli więc funkcja checkStatus () zostanie wywołana ze strony głównej, otrzymam buforowaną zawartość. Jeśli wywołam checkStatus ze strony podrzędnej, otrzymam zaktualizowaną treść.

CM Kanode
źródło
0

Podczas gdy moje strony logowania i rejestracji działają jak urok w Firefox, IE i Chrome ... Walczyłem z tym problemem w Safari dla IOS i OSX, kilka miesięcy temu znalazłem obejście dla SO.

<body onunload="">

LUB przez javascript

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

To trochę brzydka rzecz, ale działa przez jakiś czas.

Nie wiem dlaczego, ale zwracając null do onunloadzdarzenia, strona nie jest buforowana w Safari.

Adriano Rosa
źródło
0

Okazało się, że starsze iPhone'y i iPady z systemem iOS w wersji 9 i 10 czasami zwracają fałszywe, puste wyniki AJAX, być może z powodu zmniejszenia szybkości procesora przez Apple. Zwracając pusty wynik, iOS nie wywołuje serwera, tak jakby zwracał wynik z pamięci podręcznej. Częstotliwość różni się znacznie, od około 10% do 30% połączeń AJAX zwraca puste.

Trudno w to uwierzyć. Poczekaj 1s i zadzwoń ponownie. W naszych testach wystarczyło tylko jedno powtórzenie, ale napisaliśmy kod, aby wywołać go 4 razy. Nie jesteśmy pewni, czy oczekiwanie 1s jest wymagane, ale nie chcieliśmy ryzykować obciążeniem naszego serwera serią powtarzających się połączeń.

Odkryliśmy, że problem wystąpił w przypadku dwóch różnych wywołań AJAX, wywołujących różne pliki API z różnymi danymi. Ale obawiam się, że może się to zdarzyć przy każdym połączeniu AJAX. Po prostu nie wiemy, ponieważ nie sprawdzamy każdego wyniku AJAX i nie testujemy każdego połączenia wiele razy na starych urządzeniach.

Oba problematyczne wywołania AJAX używały: POST, Asynchronicznie = true, setRequestHeader = ('Content-Type', 'application / x-www-form-urlencoded')

Kiedy problem się pojawia, zwykle trwa tylko jedno wywołanie AJAX. Nie jest to spowodowane nakładającymi się połączeniami AJAX. Czasami problem występuje, gdy urządzenie jest zajęte, ale czasami nie, a bez DevTools tak naprawdę nie wiemy, co się dzieje w tym czasie.

iOS 13 tego nie robi, ani Chrome, ani Firefox. Nie mamy żadnych urządzeń testowych z systemem iOS 11 lub 12. Być może ktoś inny mógłby je przetestować?

Zwracam na to uwagę, ponieważ to pytanie jest najwyższym wynikiem Google podczas wyszukiwania tego problemu.

CaptureWiz
źródło
-1

Działa z ASP.NET dopiero po dodaniu pragma:no-cachenagłówka w IIS . Cache-Control: no-cachebyło za mało.

Boris
źródło
-2

Sugeruję obejście problemu, aby zmodyfikować sygnaturę funkcji tak, aby wyglądała mniej więcej tak:

getNewRecordID (intRecordType, strTimestamp), a następnie zawsze przekazuje również parametr TimeStamp i po prostu odrzuca tę wartość po stronie serwera. Działa to wokół problemu.

fred1234
źródło