Verhindern Sie das Caching in ASP.NET MVC für bestimmte Aktionen mithilfe eines Attributs

196

Ich habe eine ASP.NET MVC 3-Anwendung. Diese Anwendung fordert Datensätze über jQuery an. jQuery ruft eine Controller-Aktion auf, die Ergebnisse im JSON-Format zurückgibt. Ich konnte dies nicht beweisen, befürchte jedoch, dass meine Daten zwischengespeichert werden.

Ich möchte, dass das Caching nur auf bestimmte Aktionen angewendet wird, nicht auf alle Aktionen.

Gibt es ein Attribut, mit dem ich eine Aktion ausführen kann, um sicherzustellen, dass die Daten nicht zwischengespeichert werden? Wenn nicht, wie stelle ich sicher, dass der Browser jedes Mal einen neuen Satz von Datensätzen anstelle eines zwischengespeicherten Satzes erhält?

JavaScript-Entwickler
quelle
1
Wenn Sie vermuten, dass etwas zwischengespeichert wird, empfehle ich Ihnen, sich hier über die Cache-Kontrollmechanismen zu informieren

Antworten:

304

Fügen Sie auf Ihren Ajax-Methoden Folgendes ein, um sicherzustellen, dass JQuery die Ergebnisse nicht zwischenspeichert:

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

Um das Zwischenspeichern in MVC zu verhindern, haben wir unser eigenes Attribut erstellt. Sie können dasselbe tun. Hier ist unser Code:

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

Dann dekorieren Sie einfach Ihren Controller mit [NoCache]. ODER um dies für alles zu tun, können Sie das Attribut einfach auf die Klasse der Basisklasse setzen, von der Sie Ihre Controller erben (falls Sie eine haben), wie wir es hier haben:

[NoCache]
public class ControllerBase : Controller, IControllerBase

Sie können auch einige der Aktionen mit diesem Attribut dekorieren, wenn sie nicht zwischenspeicherbar sein müssen, anstatt den gesamten Controller zu dekorieren.

Wenn Ihre Klasse oder Aktion NoCachebeim Rendern in Ihrem Browser nicht vorhanden war und Sie überprüfen möchten, ob sie funktioniert, denken Sie daran, dass Sie nach dem Kompilieren der Änderungen eine "harte Aktualisierung" (Strg + F5) in Ihrem Browser durchführen müssen. Bis Sie dies tun, behält Ihr Browser die alte zwischengespeicherte Version bei und aktualisiert sie nicht mit einer "normalen Aktualisierung" (F5).

mattytommo
quelle
1
Ich habe alles in der obigen Lösung versucht und es funktioniert nicht für mich.
Obi Wan
9
Ich verstehe (und ich bin kein jQuery-Experte), dass cache: false nur dazu führt, dass jQuery das Anheften an die Abfragezeichenfolge zu einem sich ändernden Wert macht, um den Browser zu "täuschen", dass die Anforderung für etwas anderes bestimmt ist. Theoretisch bedeutet dies, dass der Browser die Ergebnisse weiterhin zwischenspeichert und die zwischengespeicherten Ergebnisse einfach nicht verwendet. Sollte auf dem Client effizienter sein, um das Caching über Antwortheader zu deaktivieren.
Josh
2
Arbeitete nur auf Controller-Ebene und nicht auf Aktionsebene.
Ramesh
3
Ich würde ein solches Attribut in das offizielle ASP.NET-Paket aufnehmen :-)
usr-local-ΕΨΗΕΛΩΝ
1
@ Frédéric, der Abschnitt der Spezifikation, auf den Sie verweisen, besagt, dass Caches keinen No-Store-Inhalt zwischenspeichern können: Die Antwortanweisung "No-Store" gibt an, dass ein Cache KEINEN Teil der sofortigen Anforderung oder Antwort speichern darf.
Kristianp
258

Sie können das integrierte Cache-Attribut verwenden, um das Caching zu verhindern.

Für .net Framework: [OutputCache(NoStore = true, Duration = 0)]

Für .net Core: [ResponseCache(NoStore = true, Duration = 0)]

Beachten Sie, dass es unmöglich ist, den Browser zu zwingen, das Caching zu deaktivieren. Das Beste, was Sie tun können, ist, Vorschläge zu machen, die die meisten Browser berücksichtigen, normalerweise in Form von Headern oder Meta-Tags. Dieses Decorator-Attribut deaktiviert das Server-Caching und fügt diesen Header hinzu : Cache-Control: public, no-store, max-age=0. Es werden keine Meta-Tags hinzugefügt. Falls gewünscht, können diese manuell in der Ansicht hinzugefügt werden.

Darüber hinaus versuchen JQuery und andere Client-Frameworks, den Browser dazu zu bringen, die zwischengespeicherte Version einer Ressource nicht zu verwenden, indem sie der URL Inhalte wie einen Zeitstempel oder eine GUID hinzufügen. Dies führt dazu, dass der Browser erneut nach der Ressource fragt, verhindert jedoch nicht wirklich das Caching.

Zum Schluss noch. Sie sollten sich bewusst sein, dass Ressourcen auch zwischen Server und Client zwischengespeichert werden können. ISPs, Proxys und andere Netzwerkgeräte zwischenspeichern ebenfalls Ressourcen und verwenden häufig interne Regeln, ohne die tatsächliche Ressource zu betrachten. Daran können Sie nicht viel ändern. Die gute Nachricht ist, dass sie normalerweise für kürzere Zeiträume wie Sekunden oder Minuten zwischengespeichert werden.

Jaguir
quelle
3
Ich glaube, dies spricht die Frage nicht vollständig an. Dies deaktiviert das ASP.NET-Caching, jedoch nicht das Browser-Caching.
Rosdi Kasim
22
Es ist unmöglich, den Browser zu zwingen, das Caching zu deaktivieren. Das Beste, was Sie tun können, ist, Vorschläge zu machen, die die meisten Browser berücksichtigen, normalerweise in Form von Headern oder Meta-Tags. Dieses Decorator-Attribut deaktiviert das Zwischenspeichern von .NET-Servern und fügt auch den Header hinzu Cache-Control:public, no-store, max-age=0. Es werden keine Meta-Tags hinzugefügt. Falls gewünscht, können diese manuell in der Ansicht hinzugefügt werden.
Jaguir
1
Ich kann verstehen , warum würden Sie verwenden NoStore = trueund Duration = 0(was ich erfolgreich eingesetzt haben, danke), aber was zusätzliche Wirkung hätte VaryByParam = "None"haben als die beiden anderen Optionen , um alle Anfragen unabhängig von Parameter beeinflussen?
Gone Coding
Ich glaube nicht, dass es in MVC erforderlich ist, ich war nur explizit. Ich erinnere mich, dass in ASP.NET-Webformularen und Benutzersteuerelementen entweder dieses Attribut oder das VaryByControl-Attribut erforderlich ist.
Jaguir
1
Für ASP.NET Core verwenden Sie: '[ResponseCache (NoStore = true, Duration = 0)]'
Jeff
48

Alles was Sie brauchen ist:

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

oder, wenn Sie es für einen gesamten Controller deaktivieren möchten:

[OutputCache(Duration=0)]
public class MyController

Trotz der Debatte in den Kommentaren hier reicht dies aus, um das Browser-Caching zu deaktivieren. Dies führt dazu, dass ASP.Net Antwortheader ausgibt, die dem Browser mitteilen, dass das Dokument sofort abläuft:

OutputCache Duration = 0 Antwortheader: max-age = 0, s-maxage = 0

Chris Moschini
quelle
6
IE8 rendert weiterhin die zwischengespeicherte Version der Seite, wenn auf die Schaltfläche "Zurück" geklickt wird, wobei nur "Dauer = 0" für eine Controller-Aktion verwendet wird. Die Verwendung von NoStore = true zusammen mit Duration = 0 (siehe Jareds Antwort) hat das Verhalten in meinem Fall behoben.
Keith Ketterer
3
Dies hat das etwas merkwürdige Verhalten der Einstellung Cache-Controlaufpublic
ta.speot.is
max-age=0hat nie "Cache deaktiviert" bedeutet. Dies bedeutet nur, dass der Antwortinhalt sofort als veraltet betrachtet wird , aber ein Cache darf ihn zwischenspeichern. Browser sollten die Aktualität des zwischengespeicherten veralteten Inhalts vor der Verwendung überprüfen. Dies ist jedoch nicht obligatorisch, es sei denn, die zusätzliche Richtlinie must-revalidateist angegeben.
Frédéric
14

Fügen Sie in der Controller-Aktion die folgenden Zeilen an die Kopfzeile an

    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.
dfortun
quelle
5

Hier ist das NoCachevon mattytommo vorgeschlagene Attribut, das durch die Verwendung der Informationen aus Chris Moschinis Antwort vereinfacht wurde:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : OutputCacheAttribute
{
    public NoCacheAttribute()
    {
        this.Duration = 0;
    }
}
Konamiman
quelle
Aus irgendeinem Grund können Sie in MVC 3 nicht nur die Dauer auf 0 setzen. Sie müssen diese Anmerkungen hinzufügen ... danke für die Problemumgehung!
Micahhoover
max-age=0hat nie "Cache deaktiviert" bedeutet. Dies bedeutet nur, dass der Antwortinhalt sofort als veraltet betrachtet wird , aber ein Cache darf ihn zwischenspeichern. Browser sollten die Aktualität des zwischengespeicherten veralteten Inhalts vor der Verwendung überprüfen. Dies ist jedoch nur dann obligatorisch, wenn die zusätzliche Richtlinie must-revalidateangegeben ist.
Frédéric
Der Vollständigkeit halber lautet die minimale und angemessenere Anweisung no-cache, die weiterhin das Zwischenspeichern ermöglicht, jedoch vor jeder Verwendung eine erneute Validierung auf dem Ursprungsserver vorschreibt. Um zu vermeiden , sogar revalidiert Caching Sie müssen hinzufügen no-storezusammen mit no-cache. ( no-storeallein ist eindeutig falsch, weil flüchtige Caches Inhalte zwischenspeichern dürfen, die als gekennzeichnet sind no-store.)
Frédéric
4

Für MVC6 ( DNX ) gibt es keineSystem.Web.OutputCacheAttribute

Hinweis: Wenn Sie den NoStoreParameter Duration einstellen , wird dies nicht berücksichtigt. Es ist möglich, eine Anfangsdauer für die Erstregistrierung festzulegen und diese mit benutzerdefinierten Attributen zu überschreiben.

Aber wir haben Microsoft.AspNet.Mvc.Filters.ResponseCacheFilter

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

Es ist möglich, den Anfangsfilter mit einem benutzerdefinierten Attribut zu überschreiben

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

Hier ist ein Anwendungsfall

    [NoCache]
    [HttpGet]
    public JsonResult Get()
    {            
        return Json(new DateTime());
    }
Davut Gürbüz
quelle
1

Ausgabe-Caching in MVC

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

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

Deepak
quelle
Siehe andere Kommentare ( 1 , 2 , 3 ) zu den zahlreichen Antworten, die bereits eine Verwendung vorschlagen. Ihre zweite Zeile ist falsch und führt bei einigen Browsern zu Problemen.
Frédéric
1

Der korrekte Attributwert für Asp.Net MVC Core , um das Zwischenspeichern von Browsern (einschließlich Internet Explorer 11 ) zu verhindern, lautet:

[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]

wie in der Microsoft-Dokumentation beschrieben:

Antwort-Caching in ASP.NET Core - NoStore und Location.None

Nenad
quelle
0

ASP.NET MVC 5-Lösungen:

  1. Die Caching Prävention Code an einer zentralen Stelle: die App_Start/FilterConfig.cs‚s - RegisterGlobalFiltersMethode:
    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. Sobald Sie dies eingerichtet haben, können Sie meines Erachtens den globalen Filter überschreiben, indem Sie eine andere OutputCacheDirektive auf Controlleroder Viewauf einer anderen Ebene anwenden . Für normale Controller ist es
[OutputCache(NoStore = true, Duration = 0, Location=System.Web.UI.ResponseCacheLocation.None, VaryByParam = "*")]

oder wenn es ein ist ApiController, wäre es

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, Location = System.Web.UI.OutputCacheLocation.None, VaryByParam = "*")]
Csaba Toth
quelle