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




jquery caching mobile-safari (21)

Mientras que mis páginas de inicio de sesión y registro funcionan como un encanto en Firefox, IE y Chrome ... He estado luchando con este problema en Safari para iOS y OSX, hace unos meses encontré una solución al SO.

<body onunload="">

O a través de javascript

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

Esto es algo feo, pero funciona por un tiempo.

No sé por qué, pero volviendo nulo al evento de onunload , la página no se almacena en caché en Safari.

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!


Después de un poco de investigación, resulta que Safari en iOS6 almacenará en caché POST que no tengan encabezados de Cache-Control o incluso "Cache-Control: max-age = 0".

La única forma que he encontrado de evitar que este almacenamiento en caché ocurra a nivel global en lugar de tener que hackear cadenas de consulta aleatorias en el final de las llamadas de servicio es establecer "Cache-Control: no-cache".

Asi que:

  • No hay encabezados de Cache-Control o Expires = iOS6 Safari almacenará en caché
  • Cache-Control max-age = 0 y un Expires inmediato = iOS6 Safari almacenará en caché
  • Control de caché: no-cache = iOS6 Safari NO se cacheará

Sospecho que Apple está aprovechando esto de la especificación HTTP en la sección 9.5 sobre POST:

Las respuestas a este método no se pueden almacenar en la memoria caché, a menos que la respuesta incluya los campos de encabezado Cache-Control o Expires apropiados. Sin embargo, la respuesta 303 (ver Otro) puede usarse para dirigir al agente de usuario a recuperar un recurso almacenable en caché.

Entonces, en teoría, puedes almacenar en la memoria caché las respuestas POST ... quién sabía. Pero ningún otro creador de navegadores ha pensado nunca que sería una buena idea hasta ahora. Pero eso NO tiene en cuenta el almacenamiento en caché cuando no se configuran los encabezados Cache-Control o Expires, solo cuando hay algún conjunto. Por lo que debe ser un error.

A continuación, se encuentra lo que uso en la parte derecha de mi configuración de Apache para apuntar a toda mi API porque a medida que sucede, en realidad no quiero almacenar en caché nada, incluso se obtiene. Lo que no sé es cómo configurar esto solo para POSTs.

Header set Cache-Control "no-cache"

Actualización: Me acabo de dar cuenta de que no señalé que es solo cuando el POST es el mismo, así que cambie cualquiera de los datos o URL del POST y estará bien. De modo que puede, como se mencionó en otra parte, simplemente agregar algunos datos aleatorios a la URL o un poco de datos POST.

Actualización: puede limitar el "no-caché" solo a POST si así lo desea en Apache:

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

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.


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

Tuve el mismo problema con una aplicación web para obtener datos del servicio web ASP.NET

Esto funcionó para mí:

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

Creo que ya resolvió su problema, pero permítame compartir una idea sobre el almacenamiento en caché web.

Es cierto que puede agregar muchos encabezados en cada idioma que use, lado del servidor, lado del cliente, y puede usar muchos otros trucos para evitar el almacenamiento en caché de la web, pero siempre piense que nunca puede saber desde dónde se conecta el cliente a su servidor. nunca se sabe si está utilizando una conexión de "punto caliente" del hotel que utiliza Squid u otros productos de almacenamiento en caché.

Si los usuarios están utilizando proxy para ocultar su posición real, etc ... la única forma real de evitar el almacenamiento en caché es la marca de tiempo en la solicitud también si no se utiliza.

Por ejemplo:

/ajax_helper.php?ts=3211321456

Luego, cada administrador de caché que debe pasar no encuentra la misma URL en el repositorio de caché y vuelve a descargar el contenido de la página.


En Sinatra de Ruby

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

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.

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!


Este fragmento de código JavaScript funciona muy bien con jQuery y jQuery Mobile:

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

Simplemente colóquelo en algún lugar de su código JavaScript (después de que se haya cargado jQuery, y antes de hacer las solicitudes de AJAX), y debería ayudar.


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.


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

}

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


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é.


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


Solución simple para todas sus solicitudes de servicio web, asumiendo que está usando 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()
    }));
});

Lea más sobre la llamada del prefiltro jQuery here .

Si no está usando jQuery, verifique los documentos para la biblioteca de su elección. Pueden tener una funcionalidad similar.


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);

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");

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

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

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

Para ASP.NET 4.5 y versiones posteriores, puede utilizar la agrupación de scripts .

La solicitud es http://localhost/MvcBM_time/bundles/AllMyScripts?v=r0sLDicvP58AIXN_mc3QdyVvVj5euZNzdsa2N1PKvb81 AllMyScripts. La cadena de consulta v tiene un token de valor que es un identificador único utilizado para el almacenamiento en caché. Mientras el paquete no cambie, la aplicación ASP.NET solicitará el paquete AllMyScripts usando este token. Si cualquier archivo en el paquete cambia, el marco de optimización ASP.NET generará un nuevo token, garantizando que las solicitudes del paquete para el navegador obtendrán el paquete más reciente.

Hay otros beneficios de la agrupación que incluyen un mayor rendimiento en la primera carga de páginas con minificación.





javascript jquery ajax caching mobile-safari