Impedire la memorizzazione nella cache in ASP.NET MVC per azioni specifiche utilizzando un attributo


196

Ho un'applicazione ASP.NET MVC 3. Questa applicazione richiede record tramite jQuery. jQuery richiama un'azione del controller che restituisce risultati in formato JSON. Non sono stato in grado di dimostrarlo, ma sono preoccupato che i miei dati possano essere memorizzati nella cache.

Voglio solo che la memorizzazione nella cache venga applicata ad azioni specifiche, non per tutte le azioni.

Esiste un attributo che posso inserire in un'azione per garantire che i dati non vengano memorizzati nella cache? In caso contrario, come posso garantire che il browser ottenga ogni volta un nuovo set di record anziché un set memorizzato nella cache?


1
Se stai indovinando che qualcosa viene memorizzato nella cache, ti consiglio di leggere i meccanismi di controllo della cache qui: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9

Risposte:


304

Per assicurarsi che JQuery non memorizzi nella cache i risultati, nei metodi ajax, inserisci quanto segue:

$.ajax({
    cache: false
    //rest of your ajax setup
});

O per evitare la memorizzazione nella cache in MVC, abbiamo creato il nostro attributo, potresti fare lo stesso. Ecco il nostro codice:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
        filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
        filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
        filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        filterContext.HttpContext.Response.Cache.SetNoStore();

        base.OnResultExecuting(filterContext);
    }
}

Quindi decora il controller con [NoCache]. O per farlo per tutto ciò che potresti semplicemente mettere l'attributo sulla classe della classe base da cui erediti i tuoi controller (se ne hai uno) come abbiamo qui:

[NoCache]
public class ControllerBase : Controller, IControllerBase

Puoi anche decorare alcune delle azioni con questo attributo se hai bisogno che non siano memorizzabili nella cache, invece di decorare l'intero controller.

Se la tua classe o azione non ha avuto NoCachequando è stato eseguito il rendering nel tuo browser e desideri verificarne il funzionamento, ricorda che dopo aver compilato le modifiche devi eseguire un "aggiornamento rapido" (Ctrl + F5) nel tuo browser. Fino a quando non lo fai, il tuo browser manterrà la vecchia versione memorizzata nella cache e non la aggiornerà con un "aggiornamento normale" (F5).


1
Ho provato di tutto nella soluzione sopra e non funziona per me.
Obi Wan,

9
È mia comprensione (e non sono un esperto di jQuery) che cache: false rende jQuery solo sulla stringa di query un valore che cambia per "ingannare" il browser nel pensare che la richiesta sia qualcos'altro. In teoria, questo significa che il browser memorizzerebbe comunque i risultati nella cache, ma non userebbe i risultati memorizzati nella cache. Dovrebbe essere più efficiente sul client disabilitare la memorizzazione nella cache tramite le intestazioni di risposta.
Josh,

2
Ha funzionato solo a livello di controller e non a livello di azione.
Ramesh,

3
Vorrei aggiungere un tale attributo nel pacchetto ASP.NET ufficiale :-)
usr-local-ΕΨΗΕΛΩΝ

1
@ Frédéric, la sezione della specifica a cui si fa riferimento afferma che la cache non può memorizzare nella cache il contenuto di nessun archivio: la direttiva di risposta "no-store" indica che una cache NON DEVE archiviare alcuna parte della richiesta o risposta immediata.
kristianp,

258

È possibile utilizzare l'attributo cache incorporato per impedire la memorizzazione nella cache.

Per .net Framework: [OutputCache(NoStore = true, Duration = 0)]

Per .net Core: [ResponseCache(NoStore = true, Duration = 0)]

Tenere presente che è impossibile forzare il browser a disabilitare la memorizzazione nella cache. Il meglio che puoi fare è fornire suggerimenti che la maggior parte dei browser rispetterà, di solito sotto forma di intestazioni o meta tag. Questo attributo decoratore disabiliterà la cache del server e anche aggiungere questa intestazione: Cache-Control: public, no-store, max-age=0. Non aggiunge meta tag. Se lo si desidera, è possibile aggiungerli manualmente nella vista.

Inoltre, JQuery e altri framework client tenteranno di indurre il browser a non utilizzare la versione cache di una risorsa aggiungendo elementi all'URL, come un timestamp o un GUID. Questo è efficace nel fare nuovamente chiedere al browser la risorsa, ma in realtà non impedisce la memorizzazione nella cache.

Un'ultima nota. È necessario tenere presente che le risorse possono anche essere memorizzate nella cache tra il server e il client. Anche gli ISP, i proxy e altri dispositivi di rete memorizzano nella cache le risorse e spesso usano regole interne senza esaminare la risorsa effettiva. Non c'è molto che puoi fare al riguardo. La buona notizia è che in genere vengono memorizzati nella cache per intervalli di tempo più brevi, come secondi o minuti.


3
Credo che questo non affronti completamente la domanda. Ciò disabilita la memorizzazione nella cache ASP.NET ma non la memorizzazione nella cache del browser.
Rosdi Kasim,

22
È impossibile forzare il browser a disabilitare la memorizzazione nella cache. Il meglio che puoi fare è fornire suggerimenti che la maggior parte dei browser rispetterà, di solito sotto forma di intestazioni o meta tag. Questo attributo decoratore disabiliterà la memorizzazione nella cache del server .NET e aggiungerà anche l'intestazione Cache-Control:public, no-store, max-age=0. Non aggiunge meta tag. Se lo si desidera, è possibile aggiungerli manualmente nella vista.
Jaguir,

1
Posso capire perché dovresti usare NoStore = truee Duration = 0(che ho usato con successo, grazie), ma quale effetto aggiuntivo avrebbe, VaryByParam = "None"poiché le altre due opzioni influenzano tutte le richieste indipendentemente dal parametro?
Codice finito il

Non credo sia necessario in MVC, ero solo esplicito. Ricordo che nei moduli Web ASP.NET e nei controlli utente è richiesto questo attributo o l'attributo VaryByControl.
Jaguir,

1
Per ASP.NET Core utilizzare: '[ResponseCache (NoStore = true, Duration = 0)]'
Jeff

48

Tutto ciò che serve è:

[OutputCache(Duration=0)]
public JsonResult MyAction(

oppure, se si desidera disabilitarlo per un intero controller:

[OutputCache(Duration=0)]
public class MyController

Nonostante il dibattito nei commenti qui, questo è sufficiente per disabilitare la memorizzazione nella cache del browser - questo fa sì che ASP.Net emetta intestazioni di risposta che indicano al browser che il documento scade immediatamente:

Durata OutputCache = 0 Header di risposta: max-age = 0, s-maxage = 0


6
IE8 esegue comunque il rendering della versione cache della pagina quando si fa clic sul pulsante Indietro utilizzando solo Durata = 0 su un'azione del controller. L'uso di NoStore = true insieme a Duration = 0 (vedi la risposta di Jared) ha risolto il problema nel mio caso.
Keith Ketterer,

3
Questo ha il comportamento alquanto curioso di impostare Cache-Controlsupublic
ta.speot.is il

max-age=0non ha mai significato "cache disabilitata". Ciò significa solo che il contenuto della risposta deve essere considerato immediatamente non aggiornato , ma una cache è autorizzata a memorizzarlo nella cache. I browser dovrebbero convalidare l'aggiornamento del contenuto non aggiornato memorizzato nella cache prima di utilizzarlo, ma non è obbligatorio a meno che non venga specificata la direttiva aggiuntiva must-revalidate.
Frédéric,

14

Nell'azione del controller aggiungere all'intestazione le seguenti righe

    public ActionResult Create(string PositionID)
    {
        Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
        Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
        Response.AppendHeader("Expires", "0"); // Proxies.

5

Ecco l' NoCacheattributo proposto da mattytommo, semplificato utilizzando le informazioni dalla risposta di Chris Moschini:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : OutputCacheAttribute
{
    public NoCacheAttribute()
    {
        this.Duration = 0;
    }
}

Per qualche motivo MVC 3 non ti consente solo di impostare la durata su 0. Devi aggiungere queste annotazioni ... grazie per la soluzione!
micahhoover,

max-age=0non ha mai significato "cache disabilitata". Ciò significa solo che il contenuto della risposta deve essere considerato immediatamente non aggiornato , ma una cache è autorizzata a memorizzarlo nella cache. I browser dovrebbero convalidare l'aggiornamento del contenuto non aggiornato memorizzato nella cache prima di utilizzarlo, ma non è obbligatorio a meno che non venga specificata la direttiva aggiuntiva must-revalidate.
Frédéric,

Per completezza, la direttiva minima e più appropriata è no-cache, che consente ancora la memorizzazione nella cache ma il mandato viene riconvalidato sul server di origine prima di qualsiasi utilizzo. Per evitare la memorizzazione nella cache anche riconvalidato si deve aggiungere no-storeinsieme no-cache. (da no-storesolo è chiaramente sbagliato perché le cache volatili sono autorizzate a memorizzare nella cache il contenuto contrassegnato come no-store.)
Frédéric

4

Per MVC6 ( DNX ), non esisteSystem.Web.OutputCacheAttribute

Nota: quando si imposta il NoStoreparametro Durata non viene considerato. È possibile impostare una durata iniziale per la prima registrazione e sovrascriverla con attributi personalizzati.

Ma noi abbiamo Microsoft.AspNet.Mvc.Filters.ResponseCacheFilter

 public void ConfigureServices(IServiceCollection services)
        ...
        services.AddMvc(config=>
        {
            config.Filters.Add(
                 new ResponseCacheFilter(
                    new CacheProfile() { 
                      NoStore=true
                     }));
        }
        ...
       )

È possibile sovrascrivere il filtro iniziale con un attributo personalizzato

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public sealed class NoCacheAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            var filter=filterContext.Filters.Where(t => t.GetType() == typeof(ResponseCacheFilter)).FirstOrDefault();
            if (filter != null)
            {
                ResponseCacheFilter f = (ResponseCacheFilter)filter;
                f.NoStore = true;
                //f.Duration = 0;
            }

            base.OnResultExecuting(filterContext);
        }
    }

Ecco un caso d'uso

    [NoCache]
    [HttpGet]
    public JsonResult Get()
    {            
        return Json(new DateTime());
    }

1

Memorizzazione nella cache di output in MVC

[OutputCache (NoStore = true, Duration = 0, Location = "None", VaryByParam = "*")]

O
[OutputCache (NoStore = true, Duration = 0, VaryByParam = "None")]


Vedi altri commenti ( 1 , 2 , 3 ) sulle numerose risposte che già suggeriscono di usarlo. La tua seconda riga è sbagliata e causerà problemi con alcuni browser.
Frédéric,


0

Soluzioni ASP.NET MVC 5:

  1. La memorizzazione nella cache il codice di prevenzione in una posizione centrale: la App_Start/FilterConfig.cs's RegisterGlobalFiltersmetodo:
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            // ...
            filters.Add(new OutputCacheAttribute
            {
                NoStore = true,
                Duration = 0,
                VaryByParam = "*",
                Location = System.Web.UI.OutputCacheLocation.None
            });
        }
    }
  1. Una volta che hai messo in atto ciò, la mia comprensione è che puoi scavalcare il filtro globale applicando una OutputCachedirettiva diversa a livello Controllero View. Per il normale controller è
[OutputCache(NoStore = true, Duration = 0, Location=System.Web.UI.ResponseCacheLocation.None, VaryByParam = "*")]

o se fosse un ApiControllersarebbe

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, Location = System.Web.UI.OutputCacheLocation.None, VaryByParam = "*")]
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.