javascript - telecharger - mathon siphon inox




Safari sur iOS 6 cache-t-il les résultats $.ajax? (17)

À partir de mon propre post de blog iOS 6.0 mise en cache des requêtes Ajax POST :

Comment le résoudre: Il existe différentes méthodes pour empêcher la mise en cache des demandes. La méthode recommandée consiste à ajouter un en-tête sans cache. C'est comme ça que c'est fait.

jQuery:

Vérifiez iOS 6.0 et définissez l'en-tête Ajax comme ceci:

$.ajaxSetup({ cache: false });

ZeptoJS:

Vérifiez iOS 6.0 et définissez l'en-tête Ajax comme ceci:

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

Du côté serveur

Java:

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

Assurez-vous d'ajouter ceci en haut de la page avant que les données ne soient envoyées au client.

.NET

Response.Cache.SetNoStore();

Ou

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.

Depuis la mise à niveau vers iOS 6, nous voyons la vue Web de Safari prendre la liberté de mettre en cache les appels $.ajax . C'est dans le contexte d'une application PhoneGap donc il utilise Safari WebView. Nos appels $.ajax sont des méthodes POST et le cache est défini sur false {cache:false} , mais cela est toujours le cas. Nous avons essayé d'ajouter manuellement un TimeStamp aux en-têtes, mais cela n'a pas aidé.

Nous avons fait plus de recherches et constaté que Safari ne restitue que les résultats mis en cache pour les services Web qui ont une signature de fonction statique et qui ne change pas d'appel à appel. Par exemple, imaginez une fonction appelée quelque chose comme:

getNewRecordID(intRecordType)

Cette fonction reçoit les mêmes paramètres d'entrée encore et encore, mais les données qu'elle retourne doivent être différentes à chaque fois.

Doit être dans la hâte d'Apple pour faire iOS 6 zip le long impressionnant, ils sont devenus trop heureux avec les paramètres de cache. Est-ce que quelqu'un d'autre a vu ce comportement sur iOS 6? Si oui, qu'est-ce qui le cause exactement?

La solution de contournement que nous avons trouvée consistait à modifier la signature de la fonction pour qu'elle ressemble à ceci:

getNewRecordID(intRecordType, strTimestamp)

puis transmettez toujours un paramètre TimeStamp , et TimeStamp simplement cette valeur du côté serveur. Cela fonctionne autour du problème. J'espère que cela aide une autre pauvre âme qui passe 15 heures sur cette question comme je l'ai fait!


Afin de résoudre ce problème pour les WebApps ajoutées à l'écran d'accueil, les deux solutions de contournement les mieux votées doivent être suivies. La mise en cache doit être désactivée sur le serveur Web pour empêcher la mise en cache de nouvelles demandes et une entrée aléatoire doit être ajoutée à chaque demande de publication pour que les demandes qui ont déjà été mises en cache soient traitées. S'il vous plaît se référer à mon message:

iOS6 - Existe-t-il un moyen de supprimer les requêtes POST ajax mises en cache pour la webapp ajoutée à l'écran d'accueil?

AVERTISSEMENT: à tous ceux qui ont implémenté une solution de contournement en ajoutant un horodatage à leurs demandes sans désactiver la mise en cache sur le serveur. Si votre application est ajoutée à l'écran d'accueil, CHAQUE réponse de post sera maintenant mise en cache, l'effacement du cache de safari ne l'efface pas et il ne semble pas expirer. À moins que quelqu'un ait un moyen de l'effacer, cela ressemble à une fuite de mémoire potentielle!


Après un peu d'enquête, il s'avère que Safari sur iOS6 cache les POST qui n'ont pas d'en-têtes Cache-Control ou même "Cache-Control: max-age = 0".

La seule façon que j'ai trouvée d'empêcher cette mise en cache de se produire au niveau global plutôt que d'avoir à pirater des chaînes de requête aléatoires sur les appels de fin de service est de définir "Cache-Control: no-cache".

Alors:

  • Aucun cache-contrôle ou expires en-têtes = iOS6 Safari sera mis en cache
  • Cache-Control max-age = 0 et un Expires immédiat = iOS6 Safari sera mis en cache
  • Cache-Control: no-cache = iOS6 Safari ne cache PAS

Je soupçonne que Apple en profite de la spécification HTTP dans la section 9.5 sur POST:

Les réponses à cette méthode ne peuvent pas être mises en cache, sauf si la réponse inclut des champs d'en-tête Cache-Control ou Expires appropriés. Toutefois, la réponse 303 (voir autre) peut être utilisée pour diriger l'agent utilisateur pour récupérer une ressource pouvant être mise en cache.

Donc, en théorie, vous pouvez mettre en cache les réponses POST ... qui savait. Mais aucun autre fabricant de navigateurs n'a jamais pensé que ce serait une bonne idée jusqu'à présent. Mais cela ne tient pas compte de la mise en cache lorsque aucun en-tête Cache-Control ou Expires n'est défini, uniquement lorsqu'il existe un ensemble. Donc ça doit être un bug.

Voici ce que j'utilise dans le bon morceau de ma configuration Apache pour cibler l'ensemble de mon API car, en fait, je ne veux pas vraiment mettre en cache quoi que ce soit. Ce que je ne sais pas, c'est comment régler ceci juste pour les POSTES.

Header set Cache-Control "no-cache"

Mise à jour: J'ai juste remarqué que je n'ai pas précisé que c'est seulement quand le POST est le même, donc changez n'importe quelle donnée de POST ou URL et tout va bien. Ainsi, comme mentionné précédemment, ajoutez simplement des données aléatoires à l'URL ou un peu de données POST.

Mise à jour: Vous pouvez limiter le "no-cache" aux POSTs si vous le souhaitez dans Apache:

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

Bien que l'ajout de paramètres de cache-buster pour que la requête semble différente semble être une solution solide, je vous déconseille de le faire, car cela nuirait à toute application qui repose sur la mise en cache réelle. Rendre les APIs en sortie les en-têtes corrects est la meilleure solution possible, même si c'est légèrement plus difficile que d'ajouter des busters de cache aux appelants.


Ceci est une mise à jour de la réponse de Baz1nga. Depuis options.data n'est pas un objet mais une chaîne que j'ai juste recouru à la concaténation de l'horodatage:

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

Cela n'a fonctionné avec ASP.NET qu'après avoir ajouté l'en pragma:no-cache tête pragma:no-cache dans IIS . Cache-Control: no-cache n'était pas suffisant.


Dans le Sinatra de Ruby

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

Des choses qui ne fonctionnent pas pour moi avec un iPad 4 / iOS 6:

Ma requête contenant: Cache-Control: no-cache

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

Ajouter un cache: false à mon appel ajax jQuery

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

Seulement cela a fait l'affaire:

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

Enfin, j'ai une solution à mon problème de téléchargement.

En JavaScript:

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

En PHP :

header('cache-control: no-cache');

J'ai été en mesure de résoudre mon problème en utilisant une combinaison de $ .ajaxSetup et en ajoutant un horodatage à l'url de mon message (pas aux paramètres post / corps). Ceci basé sur les recommandations des réponses précédentes

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

J'ai eu le même problème avec une webapp obtenir des données à partir du service web ASP.NET

Cela a fonctionné pour moi:

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

J'ai trouvé une solution qui me rend curieux de savoir pourquoi cela fonctionne. Avant de lire la réponse de Tadej concernant le service Web ASP.NET, j'essayais de trouver quelque chose qui fonctionnerait.

Et je ne dis pas que c'est une bonne solution, mais je voulais juste le documenter ici.

page principale: inclut une fonction JavaScript, checkStatus (). La méthode appelle une autre méthode qui utilise un appel jQuery AJAX pour mettre à jour le contenu html. J'ai utilisé setInterval pour appeler checkStatus (). Bien sûr, j'ai rencontré le problème de la mise en cache.

Solution: utilisez une autre page pour appeler la mise à jour.

Sur la page principale, j'ai défini une variable booléenne, runUpdate, et ajouté ce qui suit à la balise body:

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

Dans le helper.html:

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

Donc, si checkStatus () est appelé depuis la page principale, j'obtiens le contenu en cache. Si j'appelle checkStatus de la page enfant, je reçois un contenu mis à jour.


Je pense que vous avez déjà résolu votre problème, mais laissez-moi partager une idée sur la mise en cache Web.

Il est vrai que vous pouvez ajouter plusieurs en-têtes dans chaque langue que vous utilisez, côté serveur, côté client, et vous pouvez utiliser beaucoup d'autres astuces pour éviter la mise en cache web, mais pensez toujours que vous ne savez jamais d'où le client se connecte à votre serveur. vous ne savez jamais s'il utilise une connexion Hot Spot de l'hôtel qui utilise Squid ou d'autres produits de mise en cache.

Si les utilisateurs utilisent un proxy pour masquer sa position réelle, etc ... le seul moyen d'éviter la mise en cache est l'horodatage de la requête même s'il est inutilisé.

Par exemple:

/ajax_helper.php?ts=3211321456

Ensuite, chaque gestionnaire de cache que vous devez transmettre n'a pas trouvé la même URL dans le référentiel de cache et va re-télécharger le contenu de la page.


Je suggère une solution de contournement pour modifier la signature de fonction pour être quelque chose comme ceci:

getNewRecordID (intRecordType, strTimestamp), puis transmettez toujours un paramètre TimeStamp, et supprimez simplement cette valeur du côté serveur. Cela fonctionne autour du problème.


Pour ceux qui utilisent Struts 1 , voici comment j'ai résolu le problème.

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

}

Solution simple pour toutes vos demandes de service Web, en supposant que vous utilisez 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()
    }));
});

En savoir plus sur l'appel du préfiltre jQuery here .

Si vous n'utilisez pas jQuery, vérifiez les documents de la bibliothèque de votre choix. Ils peuvent avoir des fonctionnalités similaires.


Vous pouvez également résoudre ce problème en modifiant la fonction jQuery Ajax en procédant comme suit (à compter de la version 1.7.1) au début de la fonction Ajax (la fonction commence à la ligne 7212). Cette modification activera la fonctionnalité anti-cache intégrée de jQuery pour toutes les requêtes POST.

(Le script complet est disponible à http://dl.dropbox.com/u/58016866/jquery-1.7.1.js .)

Insérer ci-dessous la ligne 7221:

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

Puis modifiez ce qui suit (en commençant à la ligne ~ 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 : "");
}




mobile-safari