javascript - ¿Safari en iOS 6 almacena en caché los resultados de.ajax?




jquery caching (17)

Desde la actualización a iOS 6, estamos viendo que la vista web de Safari se toma la libertad de almacenar en caché las llamadas $.ajax . Esto está en el contexto de una aplicación PhoneGap, por lo que está utilizando Safari WebView. Nuestras llamadas $.ajax son métodos POST y tenemos el caché configurado en falso {cache:false} , pero aún así esto está sucediendo. Intentamos agregar manualmente un TimeStamp a los encabezados, pero no sirvió de nada.

Investigamos más y descubrimos que Safari solo está devolviendo resultados en caché para servicios web que tienen una firma de función que es estática y no cambia de una llamada a otra. Por ejemplo, imagina una función llamada algo como:

getNewRecordID(intRecordType)

Esta función recibe los mismos parámetros de entrada una y otra vez, pero los datos que devuelve deben ser diferentes cada vez.

Deben tener la prisa de Apple para hacer que iOS 6 zip a lo largo impresionante impresionaron demasiado con la configuración de caché. ¿Alguien más ha visto este comportamiento en iOS 6? Si es así, ¿qué es exactamente lo que lo está causando?

La solución que encontramos fue modificar la firma de la función para que fuera algo como esto:

getNewRecordID(intRecordType, strTimestamp)

y luego siempre pase también un parámetro TimeStamp , y simplemente descarte ese valor en el lado del servidor. Esto funciona alrededor del problema. ¡Espero que esto ayude a otras personas pobres que pasan 15 horas en este tema como yo!


Acabo de tener este problema también en una aplicación PhoneGap . Lo resolví utilizando la función de JavaScript getTime() de la siguiente manera:

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

Perdí unas horas resolviendo esto. Hubiera sido bueno para Apple notificar a los desarrolladores sobre este problema de almacenamiento en caché.


Cosas que no me funcionaron con un iPad 4 / iOS 6:

Mi solicitud contiene: Cache-Control: no-cache

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

Agregando caché: falso a mi llamada jQuery ajax

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

Solo esto hizo el truco:

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

Dependiendo de la aplicación, puede solucionar el problema ahora en iOS 6 usando Safari> Avanzado> Inspector web, por lo que es útil con esta situación.

Conecta el teléfono a Safari en una Mac y luego usa el menú del desarrollador para resolver problemas con la aplicación web.

Borre los datos del sitio web en el iPhone después de la actualización a iOS6, incluida la específica para la aplicación mediante una vista web. Solo una aplicación tuvo un problema y esto se resolvió durante las pruebas beta de IOS6 desde hace mucho tiempo, desde entonces no hay problemas reales.

Es posible que también deba ver su aplicación, revise NSURLCache si está en una vista web en una aplicación personalizada.

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

Supongo que dependiendo de la verdadera naturaleza de su problema, implementación, etc.

Ref: $ .ajax llama


Desde mi propia entrada de blog, iOS 6.0 almacena en caché las solicitudes POST de Ajax :

Cómo solucionarlo: Existen varios métodos para evitar el almacenamiento en caché de solicitudes. El método recomendado es agregar un encabezado sin caché. Así es como se hace.

jQuery:

Verifique iOS 6.0 y configure el encabezado Ajax de esta manera:

$.ajaxSetup({ cache: false });

ZeptoJS:

Compruebe si hay iOS 6.0 y establezca el encabezado Ajax de esta manera:

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

Lado del servidor

Java:

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

Asegúrese de agregar esto en la parte superior de la página antes de enviar los datos al cliente.

.RED

Response.Cache.SetNoStore();

O

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.

En Sinatra de Ruby

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

Encontré una solución que me da curiosidad por saber por qué funciona. Antes de leer la respuesta de Tadej sobre el servicio web ASP.NET, estaba tratando de encontrar algo que funcionara.

Y no estoy diciendo que sea una buena solución, solo quería documentarla aquí.

Página principal: incluye una función de JavaScript, checkStatus (). El método llama a otro método que utiliza una llamada jQuery AJAX para actualizar el contenido html. Utilicé setInterval para llamar a checkStatus (). Por supuesto, me encontré con el problema de almacenamiento en caché.

Solución: usa otra página para llamar a la actualización.

En la página principal, establezco una variable booleana, runUpdate, y agregué lo siguiente a la etiqueta del cuerpo:

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

En el helper.html:

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

Entonces, si se llama a checkStatus () desde la página principal, obtengo el contenido en caché. Si llamo a checkStatus desde la página secundaria, obtengo contenido actualizado.


Espero que esto pueda ser de utilidad para otros desarrolladores que golpean su cabeza contra la pared en este caso. Descubrí que cualquiera de las siguientes opciones evita que Safari en iOS 6 almacene en caché la respuesta POST:

  • agregando [cache-control: no-cache] en los encabezados de solicitud
  • añadiendo un parámetro variable de URL como la hora actual
  • agregando [pragma: no-cache] en los encabezados de respuesta
  • agregando [cache-control: no-cache] en los encabezados de respuesta

Mi solución fue la siguiente en mi Javascript (todas mis solicitudes AJAX son POST).

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

También agrego el encabezado [pragma: no-cache] a muchas de mis respuestas del servidor.

Si usa la solución anterior, tenga en cuenta que cualquier llamada $ .ajax () que realice que esté configurada como global: false NO usará la configuración especificada en $ .ajaxSetup (), por lo que deberá volver a agregar los encabezados.


Esta es una actualización de la respuesta de Baz1nga. Dado que options.data no es un objeto, sino una cadena, he recurrido para concatenar la marca de tiempo:

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

Funcionó con ASP.NET solo después de agregar el encabezado pragma:no-cache en IIS . Cache-Control: no-cache no fue suficiente.


Mi solución en ASP.NET (pagemethods, webservice, etc.)

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

Para aquellos que usan Struts 1 , aquí es cómo solucioné el problema.

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

}

Para resolver este problema para las aplicaciones web agregadas a la pantalla de inicio, se deben seguir las dos soluciones más votadas. El almacenamiento en caché debe estar desactivado en el servidor web para evitar que las nuevas solicitudes se almacenen en caché en el futuro y se debe agregar alguna entrada aleatoria a cada solicitud posterior para que las solicitudes que ya se almacenaron en caché pasen. Por favor, consulte mi mensaje:

iOS6: ¿Hay alguna forma de borrar las solicitudes POST de ajax en caché para la aplicación web agregada a la pantalla de inicio?

ADVERTENCIA: a cualquiera que haya implementado una solución agregando una marca de tiempo a sus solicitudes sin desactivar el almacenamiento en caché en el servidor. Si su aplicación se agrega a la pantalla de inicio, CADA respuesta posterior se almacenará en caché, al borrar la caché de safari no se borrará y no parece que caduque. A menos que alguien tenga una manera de eliminarlo, ¡esto parece una posible pérdida de memoria!


Pude solucionar mi problema usando una combinación de $ .ajaxSetup y agregando una marca de tiempo a la URL de mi publicación (no a los parámetros / cuerpo de la publicación). Esta basado en las recomendaciones de respuestas anteriores.

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

Si bien la adición de parámetros de caché-buster para hacer que la solicitud se vea diferente parece una solución sólida, recomendaría no hacerlo, ya que perjudicaría a cualquier aplicación que dependa del almacenamiento en caché real que tenga lugar. Hacer que las API produzcan los encabezados correctos es la mejor solución posible, incluso si es un poco más difícil que agregar busters de caché a las personas que llaman.


Sugiero una solución para modificar la firma de la función para que sea algo como esto:

getNewRecordID (intRecordType, strTimestamp) y luego siempre pasa también un parámetro TimeStamp, y simplemente descarta ese valor en el lado del servidor. Esto funciona alrededor del problema.


También puede solucionar este problema modificando la función jQuery Ajax haciendo lo siguiente (a partir de 1.7.1) en la parte superior de la función Ajax (la función comienza en la línea 7212). Este cambio activará la función integrada de antacheque de jQuery para todas las solicitudes POST.

(El script completo está disponible en http://dl.dropbox.com/u/58016866/jquery-1.7.1.js ).

Inserte debajo de la línea 7221:

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

Luego modifique lo siguiente (comenzando en la línea ~ 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 : "");
    }
}

A:

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

Una solución rápida para los servicios GWT-RPC es agregar esto a todos los métodos remotos:

getThreadLocalResponse().setHeader("Cache-Control", "no-cache");






mobile-safari