1094 votes

Est-ce que Safari sous iOS 6 met en cache les résultats de $.ajax ?

Depuis la mise à jour vers iOS 6, nous constatons que la vue web de Safari prend la liberté de mettre en mémoire cache $.ajax appels. Nous sommes dans le contexte d'une application PhoneGap et nous utilisons donc la WebView Safari. Notre site $.ajax Les appels sont POST et nous avons mis le cache à false {cache:false} mais c'est toujours le cas. Nous avons essayé d'ajouter manuellement un TimeStamp aux en-têtes mais cela n'a pas aidé.

En approfondissant nos recherches, nous avons découvert que Safari ne renvoie des résultats en cache que pour les services Web dont la signature de fonction est statique et ne change pas d'un appel à l'autre. Par exemple, imaginez une fonction appelée comme suit :

getNewRecordID(intRecordType)

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

Dans sa hâte de rendre iOS 6 impressionnant, Apple a dû être trop heureux avec les paramètres du cache. Quelqu'un d'autre a-t-il constaté ce comportement sur iOS 6 ? Si oui, quelle en est la cause exacte ?


La solution que nous avons trouvée consiste à modifier la signature de la fonction de la manière suivante :

getNewRecordID(intRecordType, strTimestamp)

et ensuite toujours passer dans un TimeStamp et de rejeter cette valeur du côté du serveur. Cela permet de contourner le problème.

1 votes

Y a-t-il une confirmation d'Apple à propos de ce problème ?

191 votes

C'est absolument choquant. Nous venons également de passer deux heures à essayer de comprendre pourquoi quelque chose a cessé de fonctionner. Notre login AJAX qui fait un POST (et qui a des en-têtes pour empêcher la mise en cache également) est mis en cache par Safari et renvoie le même JSON que la dernière fois sans même essayer le serveur... incroyable ! Nous allons devoir trouver une solution, mais il ne faut jamais mettre en cache un POST, c'est de la folie.

0 votes

Nous avons testé une application phonegap existante qui utilise Strophe pour XMPP et tout semble fonctionner sans problème.

449voto

Kieran Points 3635

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

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

Donc :

  • Pas d'en-tête Cache-Control ou Expires = iOS6 Safari mettra le cache en place
  • Cache-Control max-age=0 et une Expires immédiate = iOS6 Safari mettra en cache
  • Cache-Control : no-cache = iOS6 Safari ne mettra PAS le cache en place.

Je soupçonne qu'Apple tire parti de la section 9.5 des spécifications HTTP concernant le POST :

Les réponses à cette méthode ne peuvent pas être mises en cache, sauf si la réponse ne contienne les champs d'en-tête Cache-Control ou Expires appropriés. Cependant, la réponse 303 (See Other) peut être utilisée pour demander à l'agent utilisateur de récupérer une ressource pouvant être mise en cache.

Donc, en théorie, vous pouvez mettre en cache les réponses POST... qui l'aurait cru. Mais aucun autre fabricant de navigateur n'a jamais pensé que ce serait une bonne idée jusqu'à maintenant. Mais cela n'explique PAS la mise en cache lorsque aucun en-tête Cache-Control ou Expires n'est défini, seulement lorsque certains sont définis. Il doit donc s'agir d'un bogue.

Voici ce que j'utilise dans la partie droite de ma configuration Apache pour cibler l'ensemble de mon API, car il se trouve que je ne veux pas vraiment mettre en cache quoi que ce soit, même les résultats. Ce que je ne sais pas, c'est comment définir cela uniquement pour les POST.

Header set Cache-Control "no-cache"

Mise à jour : Je viens de remarquer 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 POST ou URL et tout va bien. Vous pouvez donc, comme mentionné ailleurs, ajouter des données aléatoires à l'URL ou un peu de données POST.

Mise à jour : Vous pouvez limiter le "no-cache" aux seuls POST si vous le souhaitez comme ceci dans Apache :

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

7 votes

Je vois où Apple veut en venir, mais nous voyons des réponses en cache à des requêtes POST même si nos réponses n'incluent pas d'en-tête Cache-Control ou Expires. Dans ce cas, iOS6 ne devrait pas mettre en cache et envoyer chaque requête. Ce n'est pas le cas.

1 votes

Oui, c'est ce que je vois aussi. Pas d'en-tête ou juste des en-têtes basés sur l'âge et il va mettre en cache, vous avez besoin d'un en-tête de type no-cache plus explicite.

139 votes

La partie de la spécification HTTP que vous avez citée ne justifie pas le comportement de mise en cache d'iOS 6. Le comportement par défaut devrait être de ne pas mettre en cache les réponses POST (c'est-à-dire lorsque l'en-tête "Cache-Control" n'est pas défini). Ce comportement est contraire à la spécification et devrait être considéré comme un bogue. Toute personne qui construit des services web api xml/json devrait décorer ses réponses POST avec "Cache-control : no-cache" pour contourner ce problème.

148voto

Dave Points 1201

J'espère que cela pourra être utile à d'autres développeurs qui se frappent la tête contre le mur sur ce sujet. J'ai découvert que l'un des éléments suivants empêche Safari sous iOS 6 de mettre en cache la réponse POST :

  • ajouter [cache-control : no-cache] dans les en-têtes de la requête
  • ajouter un paramètre URL variable tel que l'heure actuelle
  • ajouter [pragma : no-cache] dans les en-têtes de réponse
  • ajouter [cache-control : no-cache] dans les en-têtes de réponse

Ma solution était la suivante dans mon Javascript (toutes mes requêtes AJAX sont POST).

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

J'ajoute également l'en-tête [pragma : no-cache] à la plupart des réponses de mon serveur.

Si vous utilisez la solution ci-dessus, sachez que tous les appels $.ajax() que vous effectuez et qui sont définis sur global : false n'utiliseront PAS les paramètres spécifiés dans $.ajaxSetup(), vous devrez donc ajouter à nouveau les en-têtes.

4 votes

C'est LA solution CORRECTE au bug. Le problème est que iOS 6 traite les requêtes POST à partir de son cache au lieu de les envoyer au serveur. Le bogue n'est pas qu'il mette en cache les réponses aux requêtes POST (ce qui est autorisé). Si vous voulez toujours que les réponses aux requêtes POST soient extraites du cache pour les requêtes GET ultérieures vers cette URI, utilisez cette solution.

2 votes

Cela fonctionne pour moi, mais je ne comprends pas comment. J'avais déjà spécifié cache : false dans mon ajaxSetup, et en regardant les en-têtes de la requête, cela se résume à Cache-Control : no-cache et Pragma : no-cache - mais cela reste en cache sur l'iPad. Ensuite, lorsque j'ajoute des en-têtes : { "cache-control" : "no-cache" } dans ajaxSetup, il double l'en-tête Cache-Control pour qu'il soit "no-cache, no-cache" - et arrête la mise en cache. Qu'est-ce qui se passe ici ?

0 votes

Cela fonctionne parfaitement - vous pouvez également ajouter à la requête un paramètre $.ajax({type : 'POST', headers : { 'cache-control' : 'no-cache' }, etc.)

69voto

Baz1nga Points 10252

Une solution simple pour toutes vos demandes de services 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 ici .

Si vous n'utilisez pas jQuery, consultez la documentation de la bibliothèque de votre choix. Elles peuvent avoir des fonctionnalités similaires.

3 votes

Cela ne fonctionne pas pour moi, le serveur répond : "Invalid primitive JSON : timeStamp" asp.net / iis 7.5

3 votes

Qu'en est-il du $.ajax({"cache" : false ...}) ? fonctionnera-t-il car il ajoute un _=[TIMESTAMP] ? (Je ne possède pas d'appareil de ce type pour le tester).

0 votes

J'ai posté une implémentation complète de la solution proposée par Karussell. Voir ma réponse ci-dessous.

44voto

Bashevis Points 751

Je viens également de rencontrer ce problème dans un PhoneGap application. Je l'ai résolu en utilisant la fonction JavaScript getTime() de la manière suivante :

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

J'ai perdu quelques heures à résoudre ce problème. Il aurait été gentil de la part d'Apple de prévenir les développeurs de ce problème de mise en cache.

1 votes

J'allais commenter l'utilisation {cache:false} comme une option à l'un ou l'autre $.post() ou $.ajaxSetup() mais selon les docs Ces arguments sont ignorés ; jQuery ne met jamais en cache les requêtes postales, mais ne prend pas en compte le navigateur. Une meilleure option serait d'ajouter un horodatage aux requêtes en utilisant l'option $.ajaxPrefilter() .

0 votes

J'ai passé presque 5 heures à résoudre ce problème, et finalement l'ajout de l'horodatage fait l'affaire. function send_ajax(my_data,refresh) se référer ici stackoverflow.com/questions/14733772/

42voto

Tadej Points 359

J'ai eu le même problème avec une application web recevant des données d'un webservice ASP.NET.

Cela a marché pour moi :

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

2 votes

Merci beaucoup ! Je devenais fou en essayant de comprendre pourquoi l'iPhone se comportait si différemment de toutes les autres plateformes. Cette solution spécifique à ASP.NET m'a fait gagner un temps fou.

0 votes

Cela n'a pas fonctionné sur iOS6, voir ma réponse vers la fin du fil.

1 votes

S'il vous plaît !!!! Mettez une condition pour appliquer ceci seulement sur IOS 6, le cache de contenu est vital pour toute application.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X