Запобігання кешування в ASP.NET MVC для конкретних дій за допомогою атрибута


197

У мене є програма ASP.NET MVC 3. Ця програма запитує записи через jQuery. jQuery повертається до дії контролера, який повертає результати у форматі JSON. Мені це не вдалося довести, але я стурбований тим, що мої дані можуть бути кешовані.

Я хочу лише, щоб кешування було застосовано до конкретних дій, а не до всіх дій.

Чи є атрибут, який я можу вжити, щоб гарантувати, що дані не кешуються? Якщо ні, то як я можу забезпечити, щоб браузер отримував новий набір записів щоразу замість кешованого набору?


1
Якщо ви здогадуєтеся, що щось кешується, я рекомендую вам прочитати тут механізми кешування кеш- файлів

Відповіді:


304

Щоб JQuery не кешував результати, на своїх методах ajax поставте наступне:

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

Або для запобігання кешування в MVC, ми створили власний атрибут, ви можете зробити те саме. Ось наш код:

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

Тоді просто прикрасьте свій контролер [NoCache]. АБО зробити це для всього, що ви могли просто поставити атрибут класу базового класу, від якого ви успадкували контролери (якщо у вас є), як у нас тут:

[NoCache]
public class ControllerBase : Controller, IControllerBase

Ви також можете прикрасити деякі дії з цим атрибутом, якщо вам потрібно, щоб вони не були кешованими, а не прикрашали весь контролер.

Якщо ваш клас чи дія не було, NoCacheколи він відображався у вашому браузері, і ви хочете перевірити, чи працює він, пам’ятайте, що після компіляції змін вам потрібно зробити «жорстке оновлення» (Ctrl + F5) у вашому браузері. Поки ви не зробите це, ваш веб-переглядач збереже стару кешовану версію і не оновить її "нормальним оновленням" (F5).


1
Я спробував усе у наведеному вище рішенні, і це не працює для мене.
Оби Ван

9
Це моє розуміння (і я не експерт jQuery), що кешує: false лише змушує jQuery приєднуватися до рядка запиту змінним значенням, щоб "обманути" браузер думати, що запит - це щось інше. Теоретично це означає, що браузер все одно кешуватиме результати, просто не використовує кешовані результати. Клієнт повинен бути більш ефективним для відключення кешування через заголовки відповідей.
Джош

2
Працював лише на рівні контролера, а не на рівні дії.
Рамеш

3
Я б підтримав включення такого атрибуту в офіційний пакет ASP.NET :-)
usr-local-ΕΨΗΕΛΩΝ

1
@ Frédéric, розділ специфікації, на який ви вказуєте, говорить про те, що кеші не можуть кешувати вміст, який не зберігається: Директива відповідей "немає магазину" вказує, що кеш НЕ повинен зберігати жодну частину прямого запиту чи відповіді.
Крістіанп

258

Ви можете використовувати вбудований атрибут кешу, щоб запобігти кешування.

Для .net Framework: [OutputCache(NoStore = true, Duration = 0)]

Для .net Core: [ResponseCache(NoStore = true, Duration = 0)]

Майте на увазі, що неможливо змусити браузер відключити кешування. Найкраще, що ви можете зробити - це запропонувати пропозиції, які більшість браузерів шанують, як правило, у вигляді заголовків або метатегів. Цей атрибут декоратор буде відключити кешування сервера , а також додати заголовок: Cache-Control: public, no-store, max-age=0. Він не додає метатеги. За бажанням їх можна додати вручну у подання.

Крім того, JQuery та інші клієнтські рамки намагатимуться обманути браузер не використовувати кешовану версію ресурсу, додаючи в URL-адресу, наприклад, часову позначку або GUID. Це ефективно, коли браузер знову вимагає ресурс, але насправді не запобігає кешування.

На завершальну записку. Ви повинні знати, що ресурси також можуть кешуватися між сервером і клієнтом. Провайдери, проксі-сервери та інші мережеві пристрої також кешують ресурси, і вони часто використовують внутрішні правила, не дивлячись на реальний ресурс. З цим не можна багато зробити. Хороша новина полягає в тому, що вони зазвичай кешують на більш короткі часові рамки, наприклад, секунди або хвилини.


3
Я вважаю, що це не в повній мірі вирішує питання. Це вимикає кешування ASP.NET, але не кешування браузера.
Росді Касім

22
Неможливо змусити браузер відключити кешування. Найкраще, що ви можете зробити - це запропонувати пропозиції, які більшість браузерів шанують, як правило, у вигляді заголовків або метатегів. Цей атрибут декоратора відключить кешування сервера .NET, а також додасть заголовок Cache-Control:public, no-store, max-age=0. Він не додає метатеги. За бажанням їх можна додати вручну у подання.
Ягуїр

1
Я можу зрозуміти, чому б ви використовували NoStore = trueта Duration = 0(що я успішно використовую, дякую), але який додатковий ефект матиме VaryByParam = "None", оскільки два інші варіанти впливають на всі запити незалежно від параметра?
Пройшов кодування

Я не думаю, що це потрібно в MVC, я був просто явним. Я пам’ятаю, що в веб-формах ASP.NET та елементах керування користувачами цей атрибут або атрибут VaryByControl потрібен.
Ягуїр

1
Для використання ASP.NET Core: '[ResponseCache (NoStore = true, тривалість = 0)]' '
Jeff

48

Все, що тобі потрібно, це:

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

або, якщо ви хочете відключити його для цілого контролера:

[OutputCache(Duration=0)]
public class MyController

Незважаючи на дискусії в коментарях тут, цього достатньо, щоб вимкнути кешування браузера - це призводить до того, що ASP.Net видає заголовки відповідей, які повідомляють браузеру, що документ закінчується негайно:

OutputCache Тривалість = 0 Заголовки відповідей: max-age = 0, s-maxage = 0


6
IE8 як і раніше відображає кешовану версію сторінки, коли натискається кнопка "назад", використовуючи лише тривалість = 0 для дії контролера. Використання NoStore = true разом із Duration = 0 (див. Відповідь Джареда) виправило поведінку в моєму випадку.
Кіт Кеттерер

3
Це має дещо цікаву поведінку налаштування Cache-Controlнаpublic
ta.speot.is

max-age=0ніколи не означав "кеш вимкнено". Це означає лише, що вміст відповіді слід вважати негайно несвіжим , але кеш дозволений для кешування. Веб-переглядачі повинні перевірити свіжість кешованого застарілого вмісту перед його використанням, але це не є обов'язковим, якщо must-revalidateне вказана додаткова директива .
Фредерік

14

У дії контролера додайте до заголовка наступні рядки

    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

Ось NoCacheатрибут, запропонований mattytommo, спрощений за допомогою інформації з відповіді Кріса Москіні:

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

Чомусь MVC 3 не дозволяє вам встановити тривалість 0. Ви повинні додати ці примітки ... дякую за вирішення!
Міхеховер

max-age=0ніколи не означав "кеш вимкнено". Це означає лише, що вміст відповіді слід вважати негайно несвіжим , але кеш дозволений для кешування. Веб-переглядачі повинні перевірити свіжість кешованого застарілого вмісту перед його використанням, але це не є обов'язковим, якщо must-revalidateне вказана додаткова директива .
Фредерік

Для повноти є мінімальна і більш відповідна директива no-cache, яка все ще дозволяє кешувати, але мандат на переоформлення на початковому сервері перед будь-яким використанням. Щоб уникнути навіть перезатвердженого кешування, вам потрібно додати no-storeразом no-cache. ( no-storeпоодинці явно неправильно, оскільки мінливим кешам дозволено кешувати вміст, позначений як no-store.)
Фредерік

4

Для MVC6 ( DNX ) немаєSystem.Web.OutputCacheAttribute

Примітка: при встановленні NoStoreпараметра Тривалість не враховується. Можна встановити початкову тривалість для першої реєстрації та замінити це спеціальними атрибутами.

Але ми маємо Microsoft.AspNet.Mvc.Filters.ResponseCacheFilter

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

Можна змінити початковий фільтр із спеціальним атрибутом

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

Ось випадок використання

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

1

Кешування вихідних даних у MVC

[OutputCache (NoStore = true, тривалість = 0, місцеположення = "жодне", VaryByParam = "*")]

АБО
[OutputCache (NoStore = вірно, тривалість = 0, VaryByParam = "жоден")]


Дивіться інші коментарі ( 1 , 2 , 3 ) щодо численних відповідей, які вже пропонують використовувати цей варіант. Ваш другий рядок неправильний і призведе до проблем із деякими браузерами.
Фредерік


0

Рішення ASP.NET MVC 5:

  1. Кешування коду профілактики в центральному місці: в App_Start/FilterConfig.cs«S RegisterGlobalFiltersметод:
    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. Після того, як ви це встановите, ви розумієте, що ви можете змінити глобальний фільтр, застосувавши іншу OutputCacheдирективу на рівні Controllerабо Viewрівні. Для звичайного контролера це
[OutputCache(NoStore = true, Duration = 0, Location=System.Web.UI.ResponseCacheLocation.None, VaryByParam = "*")]

або якби ApiControllerце було б

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, Location = System.Web.UI.OutputCacheLocation.None, VaryByParam = "*")]
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.