Sfrutta il caching del browser in IIS (problema di google pagespeed)

Ci sono diverse domande su come sfruttare il caching del browser, ma non ho trovato nulla di utile su come farlo in un’applicazione ASP.NET. Google’s Pagespeed dice che questo è il problema più grande delle prestazioni. Finora l’ho fatto nel mio web.config :

  <!-- -->    

Il codice commentato funziona. Posso impostare l’intestazione di scadenza in un determinato momento in futuro, ma non sono stato in grado di impostare cacheControlMaxAge per impostare quanti giorni da ora il contenuto statico sarebbe stato memorizzato nella cache. Non funziona. Le mie domande sono:

Come lo posso fare? So che è ansible impostare il caching solo per una cartella specifica che sarebbe una buona soluzione, ma non funziona anche. L’applicazione è ospitata su Windows Server 2012, su IIS8, il pool di applicazioni è impostato su classico.

Dopo aver impostato questo codice in web config ho ottenuto una velocità di pagina di 72 (prima era 71). 50 file non sono stati memorizzati nella cache. (Ora 49) Mi chiedevo perché e mi sono appena accorto che un file era in realtà in cache (file svg). Sfortunatamente i file png e jpg non lo erano. Questo è il mio web.config

    
"

EDIT: Se imposto la data di scadenza esatta, il caching funziona, ma non per jpg, gif …. solo per png

EDIT2: Se imposto cacheControlCustom="public" come qui:

  

il caching funziona ma non ancora per jpeg e gif; funziona solo per svgs e pngs.

La maggior parte dei problemi di memorizzazione nella cache del browser può essere risolta visualizzando le intestazioni di risposta (può essere eseguita negli strumenti di sviluppo di Google Chrome).

inserisci la descrizione dell'immagine qui

Ora la sezione clientCache del tuo file web.config dovrebbe impostare la cache di output sull’età massima, come si vede nell’immagine qui sotto ha impostato il limite max-age a 86400 che è 1 giorno in secondi.

Ecco lo snippet web.config per questa configurazione.

  

Ora è grandioso, l’intestazione della risposta ha una proprietà massima impostata sull’intestazione Cache-Control . Quindi il browser dovrebbe memorizzare nella cache il contenuto. Beh, questo è vero principalmente, ma alcuni browser richiedono l’impostazione di un altro flag. Specificamente la flag public impostata per l’intestazione del controllo della cache. Questo può essere facilmente aggiunto usando l’attributo cacheControlCustom nel web.config . Ecco un esempio.

  

Ora quando riproviamo la pagina e ispezioniamo le intestazioni.

inserisci la descrizione dell'immagine qui

Ora come puoi vedere dall’immagine sopra ora abbiamo il valore public, max-age=86400 . Quindi il nostro browser ha tutto ciò che serve per memorizzare le risorse in cache. Ora esaminando le intestazioni e la scheda di rete di Google Chrome ci aiuterà.

Ecco la prima richiesta al file. Nota che il file non è memorizzato nella cache … inserisci la descrizione dell'immagine qui

Ora torniamo a questa pagina ( NOTA: non aggiornare la pagina, ne parleremo tra un secondo). Vedrai ora la risposta che ritorna dalla cache (come cerchiata).

inserisci la descrizione dell'immagine qui

Ora cosa succede se aggiorno la pagina utilizzando F5 o utilizzando la funzione di aggiornamento del browser. Aspetta .. dove è andato il (from cache) . inserisci la descrizione dell'immagine qui

Bene, in Google Chrome (non sono sicuro di altri browser) utilizzando il pulsante di aggiornamento sarà ansible scaricare nuovamente le risorse statiche indipendentemente dall’intestazione della cache ( inserisci qui il chiarimento per favore ). Ciò significa che le risorse sono state recuperate di nuovo e l’intestazione di età massima inviata.

Ora, dopo tutte le spiegazioni sopra, assicurati di testare come stai monitorando le intestazioni della cache.

Aggiornare

Sulla base dei tuoi commenti hai affermato di avere un gestore generico ( IHttpHandler ) di nome Image.ashx con il tipo di contenuto di image/jpg . Ora puoi aspettarti che il comportamento predefinito sia quello di mettere in cache questo gestore. Tuttavia, IIS vede l’estensione .ashx (correttamente) come uno script dinamico e non è soggetta alla memorizzazione nella cache senza impostare esplicitamente le intestazioni della cache nel codice stesso.

Ora è qui che devi stare attento, dato che in genere IHttpHandlers deve essere memorizzato nella cache poiché di solito fornisce contenuti dinamici. Ora se è improbabile che quel contenuto cambi, è ansible impostare le intestazioni della cache direttamente nel codice. Ecco un esempio di impostazione degli header della cache in IHttpHandlers usando il contesto Response .

 context.Response.ContentType = "image/jpg"; context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1)); context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.SetSlidingExpiration(true); context.Response.TransmitFile(context.Server.MapPath("~/out.jpg")); 

Ora guardando il codice stiamo impostando alcune proprietà sulla proprietà Cache . Per ottenere la risposta desiderata ho impostato le proprietà.

  • context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1)); indica la cache di out put per impostare la parte max-age= dell’intestazione di Cache-Control in 1 giorno in futuro (86400 secondi).
  • context.Response.Cache.SetCacheability(HttpCacheability.Public); dice la cache di out put per impostare l’intestazione di Cache-Control su public . Questo è abbastanza importante in quanto indica al browser di memorizzare nella cache l’object.
  • context.Response.Cache.SetSlidingExpiration(true); dice alla cache di output per assicurarsi che stia impostando correttamente la parte max-age= dell’intestazione Cache-Control . Senza impostare la scadenza di scorrimento, il caching di out out IIS ignorerà l’intestazione dell’età massima. Mettendo insieme questo mi dà questo risultato.

uscita cache dal file ashx

Come ho affermato sopra, potresti non voler memorizzare nella cache i file .ashx poiché di solito offrono contenuti dinamici. Tuttavia, se non è probabile che tale contenuto dinamico cambi in un determinato periodo, è ansible utilizzare i metodi sopra riportati per consegnare il file .ashx .

Ora, in combinazione con il processo sopra elencato, è ansible anche impostare il componente ETag (vedi wiki) delle intestazioni della cache in modo che il browser possa verificare il contenuto fornito da una stringa personalizzata. Il wiki afferma:

Un ETag è un identificatore opaco assegnato da un server web a una versione specifica di una risorsa trovata in un URL. Se il contenuto della risorsa su quell’URL cambia, viene assegnato un ETag nuovo e diverso.

Quindi questa è davvero una sorta di identificazione univoca per il browser per identificare il contenuto che viene consegnato nella risposta. Fornendo questa intestazione, il browser alla prossima ricarica invierà un’intestazione If-None-Match con l’ ETag dall’ultima risposta. Possiamo modificare il nostro gestore per rilevare l’intestazione If-None-Match e confrontarlo con il nostro Etag generato. Ora non esiste una scienza esatta per generare ETags ma una buona regola empirica è quella di fornire un identificatore che molto probabilmente definirà solo una quadro. In questo caso mi piace usare due stringhe concatenate insieme come.

 System.IO.FileInfo file = new System.IO.FileInfo(context.Server.MapPath("~/saveNew.png")); string eTag = file.Name.GetHashCode().ToString() + file.LastWriteTimeUtc.Ticks.GetHashCode().ToString(); 

Nel frammento sopra stiamo caricando un file dal nostro file system (è ansible ottenere questo da qualsiasi luogo). Sto quindi utilizzando il metodo GetHashCode() (su tutti gli oggetti) per ottenere il codice hash intero dell’object. Nell’esempio concatro l’hash del nome del file, quindi l’ultima data di scrittura. Il motivo dell’ultima data di scrittura è che nel caso in cui il file venga modificato anche il codice hash viene modificato, rendendo così le impronte digitali diverse.

Questo genererà un codice hash simile a 306894467-210133036 .

Quindi, come usiamo questo nel nostro gestore. Di seguito è riportata la versione modificata del gestore.

 System.IO.FileInfo file = new System.IO.FileInfo(context.Server.MapPath("~/out.png")); string eTag = file.Name.GetHashCode().ToString() + file.LastWriteTimeUtc.Ticks.GetHashCode().ToString(); var browserETag = context.Request.Headers["If-None-Match"]; context.Response.ClearHeaders(); if(browserETag == eTag) { context.Response.Status = "304 Not Modified"; context.Response.End(); return; } context.Response.ContentType = "image/jpg"; context.Response.Cache.SetMaxAge(TimeSpan.FromDays(1)); context.Response.Cache.SetCacheability(HttpCacheability.Public); context.Response.Cache.SetSlidingExpiration(true); context.Response.Cache.SetETag(eTag); context.Response.TransmitFile(file.FullName); 

Come puoi vedere, ho cambiato abbastanza spesso il gestore, tuttavia noterai che generiamo l’hash Etag , controlla l’intestazione If-None-Match arrivo. Se l’hash etag e l’intestazione sono uguali, diciamo al browser che il contenuto non è cambiato restituendo il codice di stato 304 Not Modified .

Successivo era lo stesso gestore tranne che aggiungiamo l’intestazione ETag chiamando:

 context.Response.Cache.SetETag(eTag); 

Quando eseguiamo questo nel browser che otteniamo.

Cache-Control con ETag

Vedrete dall’immagine (come ho fatto cambiare il nome del file) che ora abbiamo tutti i componenti del nostro sistema di cache in atto. L’ ETag viene consegnato come intestazione e il browser invia l’intestazione della richiesta If-None-Match modo che il gestore possa rispondere di conseguenza al file di cache modificato.

Usa questo. Questo è un lavoro per me.

    
 < ?xml version="1.0" encoding="UTF-8"?>        

Utilizzando quanto sopra, i file di contenuto statico verranno memorizzati nella cache per 10 giorni nel browser. Informazioni dettagliate sono disponibili qui .

Puoi anche usare l’elemento per definire le impostazioni della cache per un file specifico:

 < ?xml version="1.0" encoding="UTF-8"?>