Запретить кэширование в ASP.NET MVC для определенных действий с использованием атрибута


197

У меня есть приложение ASP.NET MVC 3. Это приложение запрашивает записи через jQuery. jQuery вызывает действие контроллера, которое возвращает результаты в формате JSON. Я не смог доказать это, но я обеспокоен тем, что мои данные могут кэшироваться.

Я хочу, чтобы кэширование применялось только к конкретным действиям, а не ко всем действиям.

Есть ли атрибут, который я могу добавить к действию, чтобы гарантировать, что данные не кэшируются? Если нет, как я могу гарантировать, что браузер каждый раз получает новый набор записей вместо кэшированного набора?


1
Если вы предполагаете, что что-то кэшируется, я рекомендую вам ознакомиться с механизмами управления кэшем здесь: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9

Ответы:


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

Вы можете использовать встроенный атрибут cache, чтобы предотвратить кеширование.

Для .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. Он не добавляет метатеги. При желании их можно добавить вручную в представлении.
Jaguir

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

Я не думаю, что это требуется в MVC, я просто был откровенен. Я помню, что в веб-формах ASP.NET и пользовательских элементах управления требуется либо этот атрибут, либо атрибут VaryByControl.
Jaguir

1
Для ASP.NET Core используйте: '[ResponseCache (NoStore = true, Duration = 0)]'
Джефф

48

Все, что тебе нужно:

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

или, если вы хотите отключить его для всего контроллера:

[OutputCache(Duration=0)]
public class MyController

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

OutputCache Duration = 0 Заголовки ответа: max-age = 0, s-maxage = 0


6
IE8 по-прежнему отображает кэшированную версию страницы, когда нажимается кнопка «Назад», используя только Duration = 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. Вы должны добавить эти аннотации ... спасибо за обходной путь!
micahhoover

max-age=0никогда не означало «кэш отключен». Это означает только то, что содержимое ответа считается немедленно устаревшим , но кэш может его кэшировать. Браузеры должны проверять свежесть кэшированного устаревшего содержимого перед его использованием, но это не обязательно, если must-revalidateне указана дополнительная директива .
Фредерик

Для полноты, минимальной и более подходящей директивой является то no-cache, что все еще разрешает кэширование, но требует повторной проверки на сервере происхождения перед любым использованием. Чтобы избежать даже повторного проверки кэширования, вы должны добавить no-storeвместе с no-cache. ( no-storeодно только явно неправильно, потому что энергозависимые кэши могут кэшировать контент, помеченный как no-store.)
Frédéric

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, Duration = 0, Location = "None", VaryByParam = "*")]

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


Смотрите другие комментарии ( 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.