it-swarm.com.de

Cacht Safari unter iOS 6 $ .ajax-Ergebnisse?

Seit dem Upgrade auf iOS 6 können in der Webansicht von Safari $.ajax Aufrufe zwischengespeichert werden. Dies erfolgt im Kontext einer PhoneGap-Anwendung, sodass Safari WebView verwendet wird. Unsere $.ajax Aufrufe sind POST Methoden und wir haben den Cache auf false {cache:false} gesetzt, aber dies geschieht immer noch. Wir haben versucht, den Headern manuell ein TimeStamp hinzuzufügen, aber es hat nicht geholfen.

Wir haben mehr Nachforschungen angestellt und festgestellt, dass Safari nur zwischengespeicherte Ergebnisse für Webdienste zurückgibt, deren Funktionssignatur statisch ist und sich nicht von Anruf zu Anruf ändert. Stellen Sie sich zum Beispiel eine Funktion mit dem Namen vor:

getNewRecordID(intRecordType)

Diese Funktion empfängt immer wieder dieselben Eingabeparameter, die zurückgegebenen Daten sollten jedoch jedes Mal anders sein.

Muss in Apples Eile sein, um iOS 6 Zip eindrucksvoll zum Laufen zu bringen. Sie waren zu zufrieden mit den Cache-Einstellungen. Hat jemand anderes dieses Verhalten unter iOS 6 gesehen? Wenn ja, was genau verursacht das?


Die gefundene Problemumgehung bestand darin, die Funktionssignatur folgendermaßen zu ändern:

getNewRecordID(intRecordType, strTimestamp)

und übergeben Sie dann immer auch einen TimeStamp -Parameter und verwerfen Sie diesen Wert einfach auf der Serverseite. Dies funktioniert um das Problem herum. Ich hoffe, das hilft einer anderen armen Seele, die sich wie ich 15 Stunden mit diesem Thema beschäftigt!

1053
user1684978

Nach einigem Nachforschen stellt sich heraus, dass Safari auf iOS6 POSTs zwischenspeichert, die entweder keine Cache-Control-Header oder sogar "Cache-Control: max-age = 0" haben.

Ich habe nur die Möglichkeit gefunden, zu verhindern, dass dieses Caching auf globaler Ebene stattfindet, anstatt zufällige Query-Ringe beim Beenden von Dienstaufrufen zu hacken, indem Sie "Cache-Control: no-cache" festlegen.

Damit:

  • Keine Cache-Kontrolle oder Expires-Header = iOS6 Safari wird zwischengespeichert
  • Cache-Control max-age = 0 und eine sofortige Expires = iOS6 Safari wird zwischengespeichert
  • Cache-Kontrolle: no-cache = iOS6 Safari wird NICHT zwischenspeichern

Ich vermute, dass Apple dies aus der HTTP-Spezifikation in Abschnitt 9.5 über POST nutzt:

Antworten auf diese Methode können nicht zwischengespeichert werden, es sei denn, die Antwort enthält die entsprechenden Felder für die Cachesteuerung oder den Ablauf der Header. Die Antwort 303 (Siehe andere) kann jedoch verwendet werden, um den Benutzeragenten anzuweisen, eine zwischenspeicherbare Ressource abzurufen.

Theoretisch können Sie also POST Antworten zwischenspeichern ... wer wusste. Aber kein anderer Browserhersteller hat jemals gedacht, dass dies eine gute Idee wäre. Dies berücksichtigt jedoch NICHT das Caching, wenn keine Cache-Control- oder Expires-Header festgelegt sind, sondern nur, wenn einige festgelegt sind. Also muss es ein Bug sein.

Nachstehend ist das aufgeführt, was ich im richtigen Teil meiner Apache-Konfiguration verwende, um auf die gesamte API zuzugreifen, da ich in diesem Fall eigentlich gar nichts zwischenspeichern möchte. Was ich nicht weiß, ist, wie man dies nur für POSTs einstellt.

Header set Cache-Control "no-cache"

Update: Ich habe gerade bemerkt, dass ich nicht darauf hingewiesen habe, dass dies nur dann der Fall ist, wenn POST identisch ist. Ändern Sie daher alle POST Daten oder URLs, und alles in Ordnung. Sie können also, wie an anderer Stelle erwähnt, der URL einfach einige zufällige Daten oder ein bisschen POST Daten hinzufügen.

Update: Sie können den "No-Cache" nur auf POSTs beschränken, wenn Sie dies in Apache wünschen:

SetEnvIf Request_Method "POST" IS_POST
Header set Cache-Control "no-cache" env=IS_POST
443
Kieran

Ich hoffe, dass dies für andere Entwickler von Nutzen sein kann, die ihren Kopf gegen die Wand schlagen. Ich habe festgestellt, dass eine der folgenden Möglichkeiten Safari unter iOS 6 daran hindert, die Antwort POST zwischenzuspeichern:

  • hinzufügen von [cache-control: no-cache] in den Anforderungsköpfen
  • hinzufügen eines variablen URL-Parameters wie der aktuellen Uhrzeit
  • hinzufügen von [Pragma: No-Cache] in den Antwort-Headern
  • hinzufügen von [cache-control: no-cache] in den Antwort-Headern

Meine Lösung war die folgende in meinem Javascript (alle meine AJAX Anfragen sind POST).

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

Ich füge auch den [Pragma: No-Cache] -Header zu vielen meiner Serverantworten hinzu.

Wenn Sie die obige Lösung verwenden, beachten Sie, dass alle $ .ajax () -Aufrufe, die Sie auf global: false setzen, NICHT die in $ .ajaxSetup () angegebenen Einstellungen verwenden, sodass Sie die Header erneut hinzufügen müssen.

145
Dave

Einfache Lösung für alle Ihre Webdienstanforderungen, vorausgesetzt, Sie verwenden 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()
    }));
});

Lesen Sie mehr über den Aufruf des jQuery-Vorfilters hier .

Wenn Sie jQuery nicht verwenden, suchen Sie in den Dokumenten nach der Bibliothek Ihrer Wahl. Sie können ähnliche Funktionen haben.

66
Baz1nga

Ich hatte gerade dieses Problem auch in einer PhoneGap -Anwendung. Ich habe es mit der JavaScript-Funktion getTime() folgendermaßen gelöst:

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

Ich habe ein paar Stunden damit verbracht, das herauszufinden. Es wäre nett von Apple gewesen, die Entwickler über dieses Caching-Problem zu informieren.

42
Bashevis

Ich hatte das gleiche Problem mit einer Webanwendung, die Daten vom ASP.NET-Webservice abruft

Das hat bei mir funktioniert:

public WebService()
{
    HttpContext.Current.Response.Cache.SetCacheability(HttpCacheability.NoCache);
    ...
}
42
Tadej

Endlich habe ich eine Lösung für mein Upload-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');
23
goker.cebeci

Aus meinem eigenen Blog-Post Caching von Ajax-Anfragen für iOS 6.0 POST:

So beheben Sie das Problem: Es gibt verschiedene Methoden, um das Zwischenspeichern von Anforderungen zu verhindern. Die empfohlene Methode ist das Hinzufügen eines No-Cache-Headers. So wird es gemacht.

jQuery:

Suchen Sie nach iOS 6.0 und legen Sie den Ajax-Header wie folgt fest:

$.ajaxSetup({ cache: false });

ZeptoJS:

Suchen Sie nach iOS 6.0 und stellen Sie den Ajax-Header wie folgt ein:

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

Serverseite

Java:

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

Stellen Sie sicher, dass dies oben auf der Seite hinzugefügt wird, bevor Daten an den Client gesendet werden.

.NETZ

Response.Cache.SetNoStore();

Oder

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.
14
kiranvj

Dieses JavaScript-Snippet funktioniert hervorragend mit jQuery und jQuery Mobile:

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

Platzieren Sie es einfach irgendwo in Ihrem JavaScript-Code (nach dem Laden von jQuery und am besten, bevor Sie AJAX Anfragen ausführen) und es sollte helfen.

7
Jonathan

Sie können dieses Problem auch beheben, indem Sie die Funktion jQueryAjax folgendermaßen (ab 1.7.1) am oberen Rand der Ajax-Funktion ändern (die Funktion beginnt in Zeile 7212) ). Diese Änderung aktiviert die integrierte Anti-Cache-Funktion von jQuery für alle POST Anforderungen.

(Das vollständige Skript ist unter http://dl.dropbox.com/u/58016866/jquery-1.7.1.js verfügbar.)

Unter der Zeile 7221 einfügen:

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

Ändern Sie dann Folgendes (beginnend mit Zeile ~ 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 : "");
    }
}

Zu:

// 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 : "");
}
6
Sam Shiles

Eine schnelle Problemumgehung für GWT-RPC-Dienste besteht darin, diese zu allen Remotemethoden hinzuzufügen:

getThreadLocalResponse().setHeader("Cache-Control", "no-cache");
5
Lars Høidahl

Dies ist ein Update von Baz1ngas Antwort. Da options.data kein Objekt ist, sondern eine Zeichenfolge, habe ich nur den Zeitstempel verkettet:

$.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();
  }
});
5
remcoder

Um dieses Problem für WebApps zu beheben, die dem Startbildschirm hinzugefügt wurden, müssen beide Problemumgehungen befolgt werden. Das Zwischenspeichern muss auf dem Webserver deaktiviert werden, damit neue Anforderungen nicht vorwärts zwischengespeichert werden. Außerdem muss jeder Post-Anforderung eine zufällige Eingabe hinzugefügt werden, damit Anforderungen, die bereits zwischengespeichert wurden, durchlaufen werden. Bitte beziehen Sie sich auf meinen Beitrag:

iOS6 - Gibt es eine Möglichkeit, zwischengespeicherte Ajax POST -Anforderungen für Webanwendungen zu löschen, die zum Startbildschirm hinzugefügt wurden?

WARNUNG: an alle, die eine Problemumgehung implementiert haben, indem Sie ihren Anforderungen einen Zeitstempel hinzugefügt haben, ohne die Zwischenspeicherung auf dem Server zu deaktivieren. Wenn Ihre App zum Startbildschirm hinzugefügt wird, wird JEDE Antwort nach dem Senden zwischengespeichert. Das Löschen des Safari-Cache löscht sie nicht und scheint nicht abzulaufen. Dies scheint ein möglicher Speicherverlust zu sein, es sei denn, jemand hat eine Möglichkeit, dies zu beheben.

4
fbader

Dinge, die für mich mit einem iPad 4/iOS 6 NICHT ARBEITETEN:

Meine Anfrage enthielt: Cache-Control: no-cache

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

Cache hinzufügen: false für meinen jQuery-Ajax-Aufruf

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

Nur das hat den Trick gemacht:

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

Das ist die Lösung für 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);    
3
Spiff

Meine Problemumgehung in ASP.NET (Seitenmethoden, Webservice usw.)

protected void Application_BeginRequest(object sender, EventArgs e)
{
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
}
2
Alexandre

Das Hinzufügen von Cache-Buster-Parametern, damit die Anforderung anders aussieht, scheint eine solide Lösung zu sein. Ich würde jedoch davon abraten, da dies jede Anwendung schädigen würde, die darauf angewiesen ist, dass tatsächlich ein Caching stattfindet. Die Ausgabe der APIs mit den richtigen Headern ist die bestmögliche Lösung, auch wenn dies etwas schwieriger ist als das Hinzufügen von Cache-Bustern zu den Aufrufern.

1
Ivo Jansch

Für diejenigen, die Struts 1 verwenden, habe ich das Problem folgendermaßen behoben.

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

}
1
cbmeeks

Ich denke, Sie haben Ihr Problem bereits behoben, aber lassen Sie mich eine Idee zum Web-Caching teilen.

Es ist wahr, dass Sie viele Header in jeder Sprache, die Sie verwenden, hinzufügen können, Server-Seite, Client-Seite, und Sie können viele andere Tricks anwenden, um Web-Caching zu vermeiden. Sie wissen nie, ob er eine Hot-Spot-Verbindung für ein Hotel verwendet, die Squid oder andere Caching-Produkte verwendet.

Wenn die Benutzer Proxy verwenden, um ihre reale Position zu verbergen, usw.… die reale einzige Möglichkeit, Caching zu vermeiden, ist der Zeitstempel in der Anforderung, auch wenn er nicht verwendet wird.

Zum Beispiel:

/ajax_helper.php?ts=3211321456

Dann hat jeder Cache-Manager, den Sie übergeben müssen, nicht die gleiche URL im Cache-Repository gefunden und lädt den Seiteninhalt erneut herunter.

1
Lanello

Ich konnte mein Problem beheben, indem ich eine Kombination von $ .ajaxSetup verwendete und einen Zeitstempel an die URL meines Posts anhängte (nicht an die Parameter/den Text des Posts). Dies basiert auf den Empfehlungen früherer Antworten

$(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');
                }
            }
        });
    });
});
1

In Rubys Sinatra

before '*' do
  if env['REQUEST_METHOD'] == 'POST'
    headers 'Cache-Control' => 'no-cache, no-store, must-revalidate'
  end
end
0
jchook

Ich habe eine Problemumgehung gefunden, die mich neugierig macht, warum es funktioniert. Bevor ich Tadejs Antwort zum ASP.NET-Webdienst las, versuchte ich, etwas zu finden, das funktionieren würde.

Und ich sage nicht, dass es eine gute Lösung ist, aber ich wollte es nur hier dokumentieren.

hauptseite: Enthält eine JavaScript-Funktion, checkStatus (). Die Methode ruft eine andere Methode auf, die einen Aufruf von jQuery AJAX verwendet, um den HTML-Inhalt zu aktualisieren. Ich habe setInterval verwendet, um checkStatus () aufzurufen. Natürlich bin ich auf das Caching-Problem gestoßen.

Lösung: Verwenden Sie eine andere Seite, um das Update aufzurufen.

Auf der Hauptseite habe ich eine boolesche Variable namens runUpdate festgelegt und Folgendes zum Tag body hinzugefügt:

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

In der von helper.html:

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

Wenn checkStatus () von der Hauptseite aufgerufen wird, erhalte ich den zwischengespeicherten Inhalt. Wenn ich checkStatus von der untergeordneten Seite aus aufrufe, erhalte ich aktualisierte Inhalte.

0
CM Kanode

Abhängig von der App können Sie das Problem jetzt in iOS 6 über Safari> Erweitert> Web Inspector beheben. Dies ist in dieser Situation hilfreich.

Verbinden Sie das Telefon mit Safari auf einem Mac und verwenden Sie dann das Entwicklermenü, um Probleme mit der Web-App zu lösen.

Löschen Sie die Websitedaten auf dem iPhone nach dem Update auf iOS6, einschließlich appspezifischer Daten, mithilfe einer Webansicht. Nur eine App hatte ein Problem und dies löste es während des IOS6 Beta-Tests vor langer Zeit, seitdem keine wirklichen Probleme.

Möglicherweise müssen Sie sich auch Ihre App ansehen und NSURLCache auschecken, wenn Sie sich in einer WebView-App in einer benutzerdefinierten App befinden.

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

Ich denke, je nach der wahren Natur Ihres Problems, Implementierung, etc. ..

Ref: $ .ajax ruft auf

0
Steven Strauss

Während meine Anmelde- und Registrierungsseiten in Firefox, IE und Chrome wie ein Zauber wirken ... Ich habe vor einigen Monaten mit diesem Problem in Safari für IOS und OSX zu kämpfen fand eine Problemumgehung auf dem SO.

<body onunload="">

ODER über Javascript

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

Das ist etwas hässlich, funktioniert aber eine Weile.

Ich weiß nicht warum, aber wenn ich beim Ereignis onunload null zurückgebe, wird die Seite in Safari nicht zwischengespeichert.

0
Adriano Rosa