Zapobiegaj buforowaniu w ASP.NET MVC dla określonych działań za pomocą atrybutu


196

Mam aplikację ASP.NET MVC 3. Ta aplikacja żąda rekordów za pośrednictwem jQuery. jQuery odwołuje się do akcji kontrolera, która zwraca wyniki w formacie JSON. Nie udało mi się tego udowodnić, ale obawiam się, że moje dane mogą być buforowane.

Chcę tylko, aby buforowanie dotyczyło określonych akcji, a nie wszystkich akcji.

Czy istnieje atrybut, który mogę zastosować w celu zapewnienia, że ​​dane nie zostaną buforowane? Jeśli nie, to jak mogę zapewnić, że przeglądarka za każdym razem otrzymuje nowy zestaw rekordów zamiast zestawu buforowanego?


1
Jeśli zgadujesz, że coś jest buforowane, zalecamy przeczytanie mechanizmów kontroli pamięci podręcznej tutaj: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9

Odpowiedzi:


304

Aby upewnić się, że JQuery nie buforuje wyników, w metodach ajax wstaw następujące:

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

Lub, aby zapobiec buforowaniu w MVC, stworzyliśmy własny atrybut, możesz zrobić to samo. Oto nasz kod:

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

Następnie po prostu udekoruj swój kontroler [NoCache]. LUB aby to zrobić, wystarczy umieścić atrybut na klasie klasy podstawowej, z której dziedziczysz kontrolery (jeśli masz taką), tak jak my tutaj:

[NoCache]
public class ControllerBase : Controller, IControllerBase

Możesz również ozdobić niektóre akcje tym atrybutem, jeśli chcesz, aby nie były one buforowane, zamiast dekorować cały kontroler.

Jeśli twoja klasa lub akcja nie miała, NoCachekiedy była renderowana w przeglądarce i chcesz sprawdzić, czy działa, pamiętaj, że po skompilowaniu zmian musisz wykonać „twarde odświeżenie” (Ctrl + F5) w przeglądarce. Do tego czasu przeglądarka zachowa starą wersję z pamięci podręcznej i nie odświeży jej za pomocą „normalnego odświeżenia” (F5).


1
Próbowałem wszystkiego w powyższym rozwiązaniu i to nie działa dla mnie.
Obi Wan

9
Rozumiem (i nie jestem ekspertem od jQuery), że buforowanie: fałsz powoduje, że jQuery zmienia ciąg zapytania w zmienną wartość, aby „oszukać” przeglądarkę, aby pomyślała, że ​​żądanie dotyczy czegoś innego. Teoretycznie oznacza to, że przeglądarka nadal zapisuje wyniki w pamięci podręcznej, po prostu nie używa wyników z pamięci podręcznej. Powinno być bardziej wydajne na kliencie, aby wyłączyć buforowanie za pomocą nagłówków odpowiedzi.
Josh

2
Działał tylko na poziomie kontrolera, a nie na poziomie akcji.
Ramesh

3
Chciałbym głosować za włączeniem takiego atrybutu do oficjalnego pakietu ASP.NET :-)
usr-local-ΕΨΗΕΛΩΝ

1
@ Frédéric, sekcja specyfikacji, na którą wskazujesz, mówi, że pamięci podręczne nie mogą buforować zawartości bez przechowywania: Dyrektywa odpowiedzi „bez przechowywania” wskazuje, że pamięć podręczna NIE MOŻE przechowywać żadnej części natychmiastowego żądania lub odpowiedzi.
kristianp

258

Możesz użyć wbudowanego atrybutu pamięci podręcznej, aby zapobiec buforowaniu.

W przypadku .NET Framework: [OutputCache(NoStore = true, Duration = 0)]

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

Pamiętaj, że nie można zmusić przeglądarki do wyłączenia buforowania. Najlepsze, co możesz zrobić, to zaproponować sugestie, które większość przeglądarek uzna, zwykle w postaci nagłówków lub metatagów. Ten atrybut dekoratora wyłączy buforowanie serwera, a także doda ten nagłówek:Cache-Control: public, no-store, max-age=0 . Nie dodaje metatagów. W razie potrzeby można je dodać ręcznie w widoku.

Ponadto JQuery i inne frameworki klienckie spróbują oszukać przeglądarkę, aby nie korzystała z buforowanej wersji zasobu, dodając elementy do adresu URL, takie jak sygnatura czasowa lub identyfikator GUID. Skutecznie powoduje to, że przeglądarka ponownie prosi o zasób, ale tak naprawdę nie zapobiega buforowaniu.

Na ostatnią notatkę. Należy pamiętać, że zasoby można również buforować między serwerem a klientem. Dostawcy usług internetowych, serwery proxy i inne urządzenia sieciowe również buforują zasoby i często używają wewnętrznych reguł bez patrzenia na rzeczywisty zasób. Niewiele można z tym zrobić. Dobrą wiadomością jest to, że zwykle buforują one w krótszych ramach czasowych, takich jak sekundy lub minuty.


3
Uważam, że nie rozwiązuje to w pełni tego pytania. Wyłącza to buforowanie ASP.NET, ale nie buforowanie w przeglądarce.
Rosdi Kasim

22
Nie można zmusić przeglądarki do wyłączenia buforowania. Najlepsze, co możesz zrobić, to zaproponować sugestie, które większość przeglądarek uzna, zwykle w postaci nagłówków lub metatagów. Ten atrybut dekoratora wyłączy buforowanie serwera .NET, a także doda nagłówek Cache-Control:public, no-store, max-age=0. Nie dodaje metatagów. W razie potrzeby można je dodać ręcznie w widoku.
Jaguir

1
Rozumiem, dlaczego byś użył NoStore = truei Duration = 0(z którego z powodzeniem korzystałem, dzięki), ale jaki dodatkowy efekt by to zrobiłVaryByParam = "None" , ponieważ pozostałe dwie opcje wpływają na wszystkie żądania niezależnie od parametru?
Gone Coding

Nie sądzę, że jest to wymagane w MVC, po prostu mówiłem wprost. Pamiętam, że w formularzach internetowych ASP.NET i kontrolach użytkownika wymagany jest albo ten atrybut, albo atrybut VaryByControl.
Jaguir

1
W przypadku ASP.NET Core: „[ResponseCache (NoStore = true, Duration = 0)]”
Jeff

48

Wszystko czego potrzebujesz to:

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

lub, jeśli chcesz go wyłączyć dla całego kontrolera:

[OutputCache(Duration=0)]
public class MyController

Pomimo debaty w komentarzach tutaj wystarczy to wyłączyć buforowanie przeglądarki - powoduje to, że ASP.Net emituje nagłówki odpowiedzi, które informują przeglądarkę, że dokument wygasa natychmiast:

OutputCache Duration = 0 Nagłówki odpowiedzi: max-age = 0, s-maxage = 0


6
IE8 nadal renderuje buforowaną wersję strony po kliknięciu przycisku Wstecz, używając tylko Czas trwania = 0 dla akcji kontrolera. Użycie NoStore = true wraz z Duration = 0 (patrz odpowiedź Jareda) naprawiło zachowanie w moim przypadku.
Keith Ketterer

3
To ma nieco dziwny zachowanie ustawienia Cache-Controldopublic
ta.speot.is

max-age=0nigdy nie oznacza „wyłączonej pamięci podręcznej”. To znaczy tylko, że treść odpowiedzi należy uznać natychmiast nieświeże , ale pamięć podręczna może ją buforować. Przeglądarki powinny zweryfikować świeżość przestarzałej zawartości w pamięci podręcznej przed jej użyciem, ale nie jest to obowiązkowe, chyba że określono dodatkową dyrektywę must-revalidate.
Frédéric,

14

W akcji kontrolera dołącz do nagłówka następujące wiersze

    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

Oto NoCacheatrybut zaproponowany przez mattytommo, uproszczony dzięki wykorzystaniu informacji z odpowiedzi Chrisa Moschiniego:

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

Z jakiegoś powodu MVC 3 nie pozwala tylko ustawić czasu trwania na 0. Musisz dodać te adnotacje ... dzięki za obejście!
micahhoover

max-age=0nigdy nie oznacza „wyłączonej pamięci podręcznej”. To znaczy tylko, że treść odpowiedzi należy uznać natychmiast nieświeże , ale pamięć podręczna może ją buforować. Przeglądarki powinny zweryfikować świeżość przestarzałej zawartości w pamięci podręcznej przed jej użyciem, ale nie jest to obowiązkowe, chyba że określono dodatkową dyrektywę must-revalidate.
Frédéric,

Dla kompletności, minimalna i bardziej odpowiednia jest dyrektywa no-cache, która nadal pozwala na buforowanie, ale upoważnia do ponownej walidacji na serwerze pochodzenia przed użyciem. Aby uniknąć nawet ponownego sprawdzania buforowania, musisz dodać no-storewraz z no-cache. ( no-storesamo jest po prostu błędne, ponieważ lotne pamięci podręczne mogą buforować zawartość oznaczoną jako no-store.)
Frédéric,

4

Dla MVC6 ( DNX ) nie maSystem.Web.OutputCacheAttribute

Uwaga: ustawienie NoStoreparametru Czas trwania nie jest brane pod uwagę. Możliwe jest ustawienie początkowego czasu trwania pierwszej rejestracji i zastąpienie go niestandardowymi atrybutami.

Ale my mamy Microsoft.AspNet.Mvc.Filters.ResponseCacheFilter

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

Możliwe jest zastąpienie początkowego filtru niestandardowym atrybutem

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

Oto przypadek użycia

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

1

Buforowanie danych wyjściowych w MVC

[OutputCache (NoStore = true, czas trwania = 0, lokalizacja = „Brak”, VaryByParam = „*”)]

LUB
[OutputCache (NoStore = true, Duration = 0, VaryByParam = „None”)]


Zobacz inne komentarze ( 1 , 2 , 3 ) do licznych odpowiedzi sugerujących użycie tego. Drugi wiersz jest nieprawidłowy i spowoduje problemy z niektórymi przeglądarkami.
Frédéric


0

Rozwiązania ASP.NET MVC 5:

  1. Buforowanie kod profilaktyki w centralnym lokalizacja: App_Start/FilterConfig.cs„s RegisterGlobalFiltersmetody:
    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. Kiedy już to zrobisz, rozumiem, że możesz zastąpić filtr globalny, stosując inną OutputCachedyrektywę na poziomie Controllerlub Viewpoziomie. Dla zwykłego kontrolera to jest
[OutputCache(NoStore = true, Duration = 0, Location=System.Web.UI.ResponseCacheLocation.None, VaryByParam = "*")]

albo jeśli to ApiControllerbędzie

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, Location = System.Web.UI.OutputCacheLocation.None, VaryByParam = "*")]
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.