Zapobiegaj buforowaniu wyników połączenia AJAX w przeglądarce

262

Wygląda na to, że jeśli ładuję zawartość dynamiczną przy użyciu $.get(), wynik jest buforowany w przeglądarce.

Dodanie losowego ciągu znaków w QueryString wydaje się rozwiązać ten problem (używam new Date().toString()), ale wydaje się, że to hack.

Czy jest jakiś inny sposób na osiągnięcie tego? Lub, jeśli unikalny ciąg jest jedynym sposobem na osiągnięcie tego, jakieś sugestie inne niż new Date()?

Salamander2007
źródło
Możesz użyć krótkiej notacji $.now()zamiast robić (new Date (). GetTime ()) za każdym razem.
Dayson
1
Twój tytuł pytania jest nieco mylący. Czy możesz rozważyć zmianę nazwy?
0112
4
Czy rozważałeś wybranie innej odpowiedzi jako zaakceptowanej?
M4N

Odpowiedzi:

241

Używam new Date().getTime(), co pozwoli uniknąć kolizji, chyba że wystąpi wiele żądań w ciągu tej samej milisekundy:

$.get('/getdata?_=' + new Date().getTime(), function(data) {
    console.log(data); 
});

Edycja: Ta odpowiedź ma kilka lat. Nadal działa (dlatego go nie usunąłem), ale istnieją teraz lepsze / czystsze sposoby osiągnięcia tego celu . Preferuję metodę, ale ta odpowiedź jest również przydatna, jeśli chcesz wyłączyć buforowanie dla każdego żądania w czasie życia strony.

Mark Bell
źródło
11
Będę głosować tylko dlatego, że łatwiej jest pozwolić jQuery zrobić to tak, jak w odpowiedzi Petera J. Twoje rozwiązanie będzie działać, ale trudniejsze do utrzymania na dłuższą metę.
Niklas Wulff
11
Jaka część tego wymaga konserwacji? W porównaniu do tego, co robi jQuery?
Sunny R Gupta
5
Warto może dodać, że new Date().getTime()kod jest wykorzystywana tak ... var nocache = new Date().getTime(); var path = 'http://hostname.domain.tld/api/somejsonapi/?cache=' + nocache;. Sam to zajęło mi kilka minut. Oczywiście ?cachemoże to być dowolne sformułowanie, którego API nie chce.
doubleJ
1
+1, nawet odpowiedź Petera J przy lepszym podejściu, ta odpowiedź nie jest zła ani nie jest złą odpowiedzią. Wierzę, że DV są zrobione, ponieważ twoje jest powyżej Petera (jak zaakceptowano). a OP nie pojawia się w SO od początku 2013 r.
Michel Ayres
1
url = url + (-1 === url.indexOf('?') ? '?' : '&') + "__=" + Number(new Date());
513

Poniższe zapobiegnie buforowaniu wszystkich przyszłych żądań AJAX, niezależnie od używanej metody jQuery ($ .get, $ .ajax itp.)

$.ajaxSetup({ cache: false });
Peter J.
źródło
7
Po zbadaniu (Fiddler) wygląda na to, że jQuery implementuje to wewnętrznie, po prostu i tak dodając znacznik czasu (jak omówiono gdzie indziej w tych odpowiedziach). Dla mnie metoda .ajaxSetup jest czystsza (moim zdaniem)
Peter J
8
Rzeczywiście nie musi znajdować się w wywołaniu gotowości dokumentu.
Peter J
19
Po co wyłączać buforowanie ajax na całym świecie? Myślę, że powinno się to odbywać dla poszczególnych połączeń, tak jak robi to odpowiedź Jonathana.
Sunny R Gupta
5
Cokolwiek działa w Twojej aplikacji. Nadal używam tej metody w aplikacjach komercyjnych, gdy absolutnie potrzebuję świeżych danych do wszystkich połączeń AJAX. W przypadku innych buforowane dane są w porządku.
Peter J
1
link Opis: Ustaw wartości domyślne dla przyszłych żądań Ajax. Jego użycie nie jest zalecane.
itwebdeveloper
319

$ .Get () JQuery zachowa wyniki w pamięci podręcznej. Zamiast

$.get("myurl", myCallback)

powinieneś użyć $ .ajax, który pozwoli ci wyłączyć buforowanie:

$.ajax({url: "myurl", success: myCallback, cache: false});
Jonathan Moffatt
źródło
62
+1 To jest poprawna odpowiedź. Rozwiązanie Peter'a J polegające na globalnym wyłączaniu buforowania jest złą praktyką IMO.
Salman von Abbas,
7
Ważne jest, aby pamiętać, że jest to „globalne” tylko dla strony / żądania.
Peter J
3
+1: buforowanie powinno być specyficzne dla typu żądania. Niektóre żądania serwera mogą wymagać buforowania (gdy dane serwera są statyczne), więc wybranie buforowania na podstawie żądania na żądanie jest lepsze niż tylko wyłączenie go .
Gone Coding
1
+1 za poprawną odpowiedź - zapobieganie buforowaniu dla poszczególnych połączeń przy użyciu metody jQuery zamiast ręcznego hakowania.
Brendan Hill
2
Kolejna dobra odpowiedź. Muszę powiedzieć, że przez większość czasu globalne wyłączenie pamięci podręcznej było bardzo korzystne. Wszystko zależy jednak od sposobu zaprojektowania aplikacji. Nie ma srebrnego punktu, ale w tej sytuacji polecam funkcję, która akceptuje wartość logiczną dla buforowania, funkcję dla wywołania zwrotnego i adres URL dla modułowości. Ręczne „hackowanie” jest w porządku, ale jeśli używasz jQuery, trzymaj się ich funkcji, gdy tylko jest to możliwe. Ułatwi to nie tylko teraz rozwój, ale także przyszłe aktualizacje biblioteki.
Anthony Mason
24

Wszystkie odpowiedzi tutaj zostawiają ślad na żądanym adresie URL, który pojawi się w dziennikach dostępu serwera.

Potrzebowałem rozwiązania opartego na nagłówkach bez efektu ubocznego i odkryłem, że można to osiągnąć, konfigurując nagłówki wymienione w temacie Kontrolowanie buforowania stron internetowych we wszystkich przeglądarkach? .

Rezultat, działający przynajmniej dla Chrome, byłby następujący:

$.ajax({
   url: url, 
   headers: {
     'Cache-Control': 'no-cache, no-store, must-revalidate', 
     'Pragma': 'no-cache', 
     'Expires': '0'
   }
});

Aidin
źródło
Może to głupie pytanie, ale jeśli moje aukcje zwrócą obrazy, czy obrazy zostaną zbuforowane? Aby uniknąć masowych żądań Amazon S3?
Marcelo Agimóvel
Marcelo Agimóvel, wierzę, że może to być osobne pytanie SO.
Aidin
23

innym sposobem jest brak nagłówków pamięci podręcznej od strony serwera w kodzie, który generuje odpowiedź na wywołanie ajax:

response.setHeader( "Pragma", "no-cache" );
response.setHeader( "Cache-Control", "no-cache" );
response.setDateHeader( "Expires", 0 );
miceuz
źródło
17
Błędny. W IE nagłówki no-cache są ignorowane dla wywołań XMLHttpRequest, jak omówiono tutaj: stackoverflow.com/questions/244918/ ... DateTime (lub moja metoda .ajaxSetup) to jedyne rozwiązania, które faktycznie działają.
Peter J
właśnie wkleiłem moją zwykłą mantrę bez pamięci podręcznej, nie jest powiedziane, że jest specyficzna dla IE
miceuz
2
Powinno to wyłączyć buforowanie dla wszystkich przeglądarek: response.setHeader („Cache-Control”, „max-age = 0, no-cache, no-store, post-check = 0, pre-check = 0”);
Chris Broski,
13

Osobiście uważam, że metoda ciągu zapytania jest bardziej niezawodna niż próba ustawienia nagłówków na serwerze - nie ma gwarancji, że serwer proxy lub przeglądarka i tak go nie buforuje (niektóre przeglądarki są gorsze od innych - bez nazw).

Zwykle używam, Math.random()ale nie widzę nic złego w używaniu daty (nie powinieneś robić żądań AJAX wystarczająco szybko, aby uzyskać tę samą wartość dwa razy).

Greg
źródło
2
Połącz Date (). GetTime () razem z Math.random () i powinieneś być po bezpiecznej stronie. Na marginesie, Ext.Ajax używa również getTime (), gdy określono opcję disableCaching.
vividos,
12

Zgodnie z dokumentacją: http://api.jquery.com/jquery.ajax/

możesz użyć cachenieruchomości z:

$.ajax({
    method: "GET",
    url: "/Home/AddProduct?",
    data: { param1: value1, param2: value2},
    cache: false,
    success: function (result) {
        // TODO
    }
});
MrMins
źródło
5

Oczywiście techniki „łamania pamięci podręcznej” wykonają zadanie, ale nie zdarzyłoby się to przede wszystkim, gdyby serwer wskazał klientowi, że nie należy buforować odpowiedzi. W niektórych przypadkach korzystne jest buforowanie odpowiedzi, czasem nie. Pozwól serwerowi decydować o poprawnym czasie życia danych. Możesz to zmienić później. Znacznie łatwiej jest to zrobić z serwera niż z wielu różnych miejsc w kodzie interfejsu użytkownika.

Oczywiście to nie pomaga, jeśli nie masz kontroli nad serwerem.

Mark Renouf
źródło
5

Co powiesz na użycie żądania POST zamiast GET ...? (Które i tak powinieneś ...)

Thomas Hansen
źródło
Myślę, że to lepsze rozwiązanie, ale niestety (jakoś) mogę wykonać tylko żądanie GET. Więc .. to na razie nowa Date (). GetTime ().
Salamander2007
Dodaj wyjaśnienie do swojej odpowiedzi, aby inni mogli się z niej uczyć - dlaczego trzeba żądać POST?
Nico Haase
5

Prawdziwe pytanie brzmi: dlaczego nie trzeba tego buforować? Jeśli nie należy go buforować, ponieważ cały czas się zmienia, serwer powinien określić, aby nie buforować zasobu. Jeśli czasem się po prostu zmienia (ponieważ jeden z zasobów, od których zależy, może się zmienić), a jeśli kod klienta ma sposób o tym wiedzieć, może dołączyć parametr zastępczy do adresu URL, który jest obliczany z pewnego skrótu lub daty ostatniej modyfikacji tych zasobów (to właśnie robimy w zasobach skryptu Microsoft Ajax, aby można je było zawsze przechowywać w pamięci podręcznej, ale nowe wersje mogą być nadal wyświetlane, gdy się pojawią). Jeśli klient nie może wiedzieć o zmianach, serwer powinien poprawnie obsługiwać żądania HEAD i informować klienta, czy ma używać wersji buforowanej, czy nie. Wydaje mi się, że dodawanie losowego parametru lub mówienie klientowi, aby nigdy nie buforować, jest błędne, ponieważ buforowanie jest właściwością zasobu serwera i dlatego powinno się decydować po stronie serwera. Innym pytaniem, które należy sobie zadać, jest to, czy ten zasób powinien być rzeczywiście obsługiwany przez GET, czy też powinien przejść przez POST? Jest to kwestia semantyki, ale ma również wpływ na bezpieczeństwo (są ataki, które działają tylko wtedy, gdy serwer pozwala na GET). POST nie zostanie zbuforowany.


źródło
6
Co jeśli przechodzisz przez serwery proxy, których nie kontrolujesz ich zasad buforowania? co jeśli Twoja aplikacja musi za każdym razem robić nowe żądanie? Odpowiedź na różne rzeczy nie zawsze jest wyraźna czarno-biała, zawsze są szare obszary.
7wp
To prawda, że ​​nie zawsze jest to jednoznaczne. Ale widok tej odpowiedzi zmusił mnie do zakwestionowania moich założeń i doprowadził mnie do znalezienia głównej przyczyny mojego problemu. To może nie dotyczyć wszystkich, ale pomogło mi. Jeśli tutaj czytasz to, powinieneś to również rozważyć.
Jonathan Tran
Pomogło mi to, ResponseCaching został domyślnie ustawiony na 60m po stronie serwera. Zmieniłem go na No-Cache i przestał buforować na kliencie.
mattygee
4

Może zamiast tego powinieneś spojrzeć na $ .ajax () (jeśli używasz jQuery, jak to wygląda). Spójrz na: http://docs.jquery.com/Ajax/jQuery.ajax#options i opcję „cache”.

Innym podejściem byłoby przyjrzenie się, jak buforujesz rzeczy po stronie serwera.

finpingvin
źródło
1
Niestety, po pewnym dochodzeniu użycie $ .ajax () i set cache = false w zasadzie spowoduje to samo. jQuery doda do kwerendy pewną liczbę losową i nie sprawdza istniejącej kwerendy. Myślę, że wystarczy $ .get ().
Salamander2007
No dobra. Nigdy tego nie próbowałem, po prostu pamiętałem, że coś o tym widziałem w dokumentacji :)
finpingvin
Nie trzeba nawet używać $ .ajax. Wystarczy użyć .ajaxSetup.
Peter J
3

Mały dodatek do podanych doskonałych odpowiedzi: Jeśli korzystasz z rozwiązania do tworzenia kopii zapasowych innych niż ajax dla użytkowników bez javascript, będziesz musiał poprawić nagłówki po stronie serwera. Nie jest to niemożliwe, choć rozumiem tych, którzy się poddają;)

Jestem pewien, że jest jeszcze jedno pytanie dotyczące SO, które da ci pełny zestaw odpowiednich nagłówków. Nie jestem do końca przekonany, że odpowiedź myszy obejmuje wszystkie bazy w 100%.

krosenvold
źródło
3

Dla tych z was, którzy korzystają z cacheopcji $.ajaxSetup()mobilnego Safari, może być konieczne użycie znacznika czasu dla testów POST, ponieważ mobilne Safari również to buforuje. Zgodnie z dokumentacją $.ajax()(do której jesteś kierowany $.ajaxSetup()):

Ustawienie wartości false w pamięci podręcznej będzie działać poprawnie tylko z żądaniami HEAD i GET. Działa poprzez dołączenie „_ = {timestamp}” do parametrów GET. Ten parametr nie jest potrzebny w przypadku innych typów żądań, z wyjątkiem IE8, gdy POST jest wysyłany do adresu URL, który został już zażądany przez GET.

Samo ustawienie tej opcji nie pomoże ci w przypadku, o którym wspomniałem powyżej.

Athasach
źródło
2

Zasadniczo po prostu dodaj cache:false;w ajax, gdzie Twoim zdaniem zawartość się zmieni w miarę postępu. A w miejscu, w którym treść się nie zmieni, możesz to pominąć. W ten sposób otrzymasz nową odpowiedź za każdym razem

Santosh Upadhayay
źródło
2

Teraz łatwo to zrobić, włączając / wyłączając opcję pamięci podręcznej w żądaniu ajax, podobnie jak to

$(function () {
    var url = 'your url goes here';
    $('#ajaxButton').click(function (e) {
        $.ajax({
            url: url,
            data: {
                test: 'value'
            },
                cache: true, //cache enabled, false to reverse
                complete: doSomething
            });
        });
    });
    //ToDo after ajax call finishes
    function doSomething(data) {
        console.log(data);
    }
});
Omar El Don
źródło
3
Po 6 latach odpowiadasz tak samo jak Jonathan? ಠ_ಠ
redent84
ludzie mogą powiedzieć, że minęło 6 lat od opublikowania pytania. Moja odpowiedź na to pytanie różni się od innych, nie wspominając już o tym, że jest poprawna w dzisiejszych czasach. Odpowiedź na takie pytania nie jest przeznaczona dla „pytającego”, ale dla społeczności i początkujących! Dzięki za dodanie wyjaśnienia!
Omar El Don
Jaka jest różnica między twoim a tym jednym stackoverflow.com/a/735084/469218 ?
redent84
może to jest klarowność z punktu widzenia początkującego zadającego takie pytanie !!
Omar El Don
1

Jak powiedział @Athasach, zgodnie z dokumentami jQuery: $.ajaxSetup({cache:false}) nie będzie działać dla innych niż żądania GET i HEAD.

Lepiej odeślij Cache-Control: no-cache każdym razie nagłówek z serwera. Zapewnia bardziej przejrzysty podział problemów.

Oczywiście nie działałoby to w przypadku adresów URL usług, które nie należą do Twojego projektu. W takim przypadku możesz rozważyć udostępnienie usługi innej firmy za pomocą kodu serwera zamiast wywoływania jej z kodu klienta.

rstackhouse
źródło
1

Jeśli używasz .NET ASP MVC, wyłącz buforowanie akcji kontrolera, dodając następujący atrybut do funkcji punktu końcowego:

[OutputCacheAttribute(VaryByParam = "*", Duration = 0, NoStore = true)]
Marius
źródło
Czy możesz to wyjaśnić dalej? Jak ta tablica odnosi się do AJAX?
Nico Haase
To nie jest tablica, to atrybut akcji kontrolera MVC.
Marius
0

dodaj nagłówek

headers: {
                'Cache-Control':'no-cache'
            }
Moaz Salem
źródło
Dodaj wyjaśnienie do swojej odpowiedzi, aby inni mogli się z niej uczyć - gdzie należy dodać takie nagłówki?
Nico Haase
-3

dołącz Math.random() do adresu URL żądania

xiaoyifang
źródło
2
To da niestabilne wyniki.
aj.toulan
Math.random będzie działał tylko jako parametr, jak url? _ = [Math.Random ()], nie ma to nic wspólnego z niestabilnym wynikiem.
xiaoyifang
4
Rozumiem co robiłeś. Ja tylko komentowałem, że Math.Random () czasami da ci dwukrotnie ten sam numer. Jeśli wypełnisz swój system niepewnościami, dodadzą się one tylko jeden na drugim.
aj.toulan