ASP.NET MVC и кэширование IE – управление заголовками ответов неэффективно

Задний план

Я пытаюсь помочь коллеге отладить проблему, которая не была проблемой в течение последних 6 месяцев. После последнего развертывания приложения ASP.NET MVC 2 ответы FileResult которые вынуждают PDF-файл у пользователя открывать или сохранять, имеют проблемы, которые достаточно долго сохраняются на клиентской машине для чтения PDF-файлов, чтобы открыть их.

Более ранние версии IE (особенно 6) являются единственными браузерами. Firefox и Chrome и более новые версии IE (> 8) ведут себя так, как ожидалось. Имея это в виду, следующий раздел определяет действия, необходимые для воссоздания проблемы.

Поведение

  1. Пользователь нажимает ссылку, указывающую на метод действия (простая гиперссылка с атрибутом href ).
  2. Метод действия генерирует PDF, представляемый в виде streamа байтов. Метод всегда воссоздает PDF.
  3. В методе действий заголовки настроены на указание браузерам, как кэшировать ответ. Они есть:

     response.AddHeader("Cache-Control", "public, must-revalidate, post-check=0, pre-check=0"); response.AddHeader("Pragma", "no-cache"); response.AddHeader("Expires", "0"); 

    Для тех, кто не знаком с тем, что делают заголовки :

    а. Кэш-контроль: общеansible

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

    б. Cache-Control: необходимо-revalidate

    Когда директива must-revalidate присутствует в ответе, полученном кешем, этот кеш НЕ ДОЛЖЕН использовать эту запись после того, как она станет устаревшей, чтобы ответить на последующий запрос без предварительной проверки ее с помощью исходного сервера

    с. Cache-Control: предварительная проверка (введенная с IE5)

    Определяет интервал в секундах, после которого объект должен быть проверен на свежесть. Проверка может произойти после того, как пользователь покажет ресурс, но гарантирует, что в следующем обратном просмотре кешированная копия будет обновлена.

    д. Cache-Control: после проверки (введено с IE5)

    Определяет интервал в секундах, после которого сущность должна быть проверена на свежесть, прежде чем показывать пользователю ресурс.

    е. Pragma: no-cache (для обеспечения обратной совместимости с HTTP / 1.0)

    Когда директива no-cache присутствует в сообщении запроса, приложение ДОЛЖНА пересылать запрос на исходный сервер, даже если у него есть кэшированная копия того, что запрашивается

    е. Истекает

    Поле Expires entity-header указывает дату / время, после которого ответ считается устаревшим.

  4. Мы возвращаем файл из действия

     return File(file, "mime/type", fileName); 
  5. Пользователь получает диалоговое окно «Открыть / Сохранить»

  6. Нажатие «Сохранить» работает так, как ожидалось, но нажатие «Открыть» запускает PDF-ридер, но временный файл IE, хранящийся, уже был удален к моменту, когда читатель попытается открыть файл, поэтому он жалуется, что файл отсутствует (и он является).

Здесь есть еще полдюжины других приложений, которые используют одни и те же заголовки, чтобы заставить Excel, CSV, PDF, Word и тонну другого контента у пользователей, и никогда не было проблемы.

Вопрос

  • Правильны ли заголовки для того, что мы пытаемся сделать? Мы хотим, чтобы файл временно существовал (получал кеширование), но всегда заменялся новыми версиями, хотя запросы могут быть идентичными).

Заголовки ответов устанавливаются в методе действий перед возвратом FileResult . Я попросил моего коллегу попытаться создать новый class, который наследует от FileResult и вместо этого переопределить метод ExecuteResult чтобы он изменял заголовки, а затем вместо этого base.ExecuteResult() – нет статуса.

У меня есть догадка, что заголовок «Истекает» «0» является виновником. Согласно этой статье W3C установка «0» подразумевает «уже истек». Я хочу, чтобы он истек, я просто не хочу, чтобы IE удалял его из файловой системы до того, как обработчик приложения получит возможность открыть его.

Как всегда, спасибо!

Изменить: решение

При дальнейшем тестировании (используя Fiddler для проверки заголовков) мы видели, что заголовки ответов, которые, как мы думали, устанавливались, не были интерпретированы браузером. Не знакомый с кодом сам, я не знал о базовой проблеме: заголовки попадали на поверхность вне метода действия.

Тем не менее, я собираюсь оставить этот вопрос открытым. И все-таки выдающийся это: похоже, существует некоторая разница между заголовком Expires имеющим значение 0 сравнению с -1 . Если кто-то может претендовать на различия по дизайну, то в отношении IE я все равно хотел бы услышать об этом. Что касается решения, то вышеупомянутые заголовки работают так, как предполагалось, с значением Expires установленным на -1 во всех браузерах.

Обновление 1

Сообщение Как контролировать кэширование веб-страниц во всех браузерах? подробно описывает, что кэширование можно предотвратить во всех браузерах с помощью установки Expires = 0. Я все еще не продаюсь в этом аргументе 0 против -1

3 Solutions collect form web for “ASP.NET MVC и кэширование IE – управление заголовками ответов неэффективно”

Я думаю, вы должны просто использовать

 HttpContext.Current.Response.Cache.SetMaxAge (new TimeSpan (0)); 

или

 HttpContext.Current.Response.Headers.Set ("Cache-Control", "private, max-age=0"); 

для установки max-age=0 что больше не означает повторной проверки кеша (см. здесь ). Если вы ETag в заголовок с некоторой пользовательской контрольной суммой hashа из данных, ETag из предыдущего запроса будет отправлен на сервер. Сервер может либо возвращать данные, либо, в случае, если данные точно такие же, как и раньше, он может возвращать пустой код и HttpStatusCode.NotModified в качестве кода состояния. В случае, если веб-браузер получит данные из локального кеша браузера.

Я рекомендую вам использовать Cache-Control: private которые заставляют две важные вещи: 1) отключить кеширование данных на прокси, который иногда имеет очень агрессивные настройки кэширования 2) он позволит кэшировать данные, но не разрешать совместное использование кэш с другими пользователями. Он может решить проблемы конфиденциальности, поскольку данные, которые вы возвращаете одному пользователю, не могут быть прочитаны другими пользователями. Кстати, код HttpContext.Current.Response.Cache.SetMaxAge (new TimeSpan (0)) по умолчанию устанавливает Cache-Control: private, max-age=0 в заголовке HTTP. Если вы хотите использовать Cache-Control: public вы можете использовать SetCacheability (HttpCacheability.Public); для перезаписывания поведения или использования Headers.Set вместо Cache.SetMaxAge .

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

ОБНОВЛЕНО : я решил написать дополнительную информацию, чтобы очистить свою позицию. Соответствует информации из Википедии, даже такие старые веб-браузеры, как Mosaic 2.7, Netscape 2.0 и Internet Explorer 3.0, поддерживают в марте 1996 года предварительный стандарт HTTP / 1.1, описанный в RFC 2068. Поэтому я предполагаю (но не проверять его), что старый веб-браузеры поддерживают max-age=0 HTTP-заголовок. В любом случае Netscape 2.06 и Internet Explorer 4.0 окончательно поддерживают HTTP 1.1.

Поэтому сначала вы должны спросить: какие стандарты HTML вы используете? Вы все еще используете HTML 2.0 вместо более позднего HTML 3.2, опубликованного в январе 1997 года? Я полагаю, вы используете хотя бы HTML 4.0, опубликованный в декабре 1997 года. Поэтому, если вы создадите приложение, по крайней мере, в HTML 4.0, ваш сайт может быть ориентирован на веб-клиентов, поддерживающих HTTP 1.1, и игнорировать (не поддерживать) веб-клиентов, которые не поддерживают HTTP 1.1.

Теперь о других заголовках «Cache-Control» как «private, max-age = 0». Включение заголовков – это, на мой взгляд, чистая паранойя . Поскольку у меня есть проблема с кешированием, я попытался также включить другие заголовки, но позже, внимательно прочитав раздел 14.9 RFC2616, я использую только «Cache-Control: private, max-age = 0».

Единственный заголовок «Cache-Control», который может быть дополнительно обсужден, «должен-revalidate», описанный в разделе 14.9.4, на который я ссылался ранее. Вот цитата:

Директива must-revalidate необходима для поддержки надежной работы определенных функций протокола. При любых обстоятельствах кеш HTTP / 1.1 ДОЛЖЕН подчиняться директиве must-revalidate; в частности, если по какой-либо причине кеш не может дойти до исходного сервера, он ДОЛЖЕН создать ответ 504 (Gateway Timeout).

Серверу СЛЕДУЕТ присылать директиву must-revalidate в том и только в том случае, если отказ от повторной проверки запроса на объект может привести к неправильной работе, такой как беспроблемная финансовая транзакция. Получатели НЕ ДОЛЖНЫ выполнять какие-либо автоматические действия, нарушающие эту директиву, и НЕ ДОЛЖНЫ автоматически предоставлять необоснованную копию объекта, если повторная аттестация не выполняется.

Хотя это не рекомендуется, пользовательские агенты, работающие под жесткими ограничениями на подключение, могут нарушать эту директиву, но, если это так, ДОЛЖНЫ явно предупредить пользователя о том, что был предоставлен неутвержденный ответ. Предупреждение ДОЛЖНО быть предоставлено при каждом неуказанном доступе, и СЛЕДУЕТ потребовать явного подтверждения пользователя.

Когда-нибудь, если у меня возникнут проблемы с подключением к Интернету, я вижу пустую страницу с сообщением «Время ожидания шлюза». Он исходит из использования директивы «must-revalidate». Я не думаю, что сообщение «Gateway Timeout» действительно помогает пользователю.

Таким образом, люди, как предпочитают начинать саморазрушительную процедуру, если он слышит сигнал «Занято» по вызову своему боссу, должны дополнительно использовать директиву «must-revalidate» в заголовке «Cache-Control». Другие лица, я рекомендую использовать «Cache-Control: private, max-age = 0» и ничего больше.

Я знаю, что это поздно, но эта ссылка может быть огромной помощью для всех, кто интересуется темой: http://dotnet.dzone.com/articles/output-caching-aspnet-mvc

Для IE я помню, что нужно установить Expires: -1 . Как предотвратить кеширование в Internet Explorer, похоже, подтверждает это с помощью следующего fragmentа кода.

 < % Response.CacheControl = "no-cache" %> < % Response.AddHeader "Pragma", "no-cache" %> < % Response.Expires = -1 %> 

Оглядываясь назад в коде, это то, что я нашел. Кроме того, я смутно помню, что если вы установите Cache-Control: private может не корректно вести себя с SSL.

 Response.AddHeader("Cache-Control", "no-cache"); Response.AddHeader("Expires", "-1"); 

Также, так, вы не хотите кэш, а? упоминает -1 , но вместо этого использует методы Response.Cache :

 // Stop Caching in IE Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache); // Stop Caching in Firefox Response.Cache.SetNoStore(); 

Однако проблема ASP-кэширования (IE8) говорит, что этот код не работает.

  • Поиск простого кэша Java в памяти
  • Как кэшировать данные в приложении MVC
  • WPF TabControl - предотrotation разгрузки при изменении вкладки?
  • API с легким API-интерфейсом Java
  • Как бы вы реализовали кеш LRU в Java?
  • Каков наилучший метод для принудительного истечения кеша в ASP.NET?
  • Что такое __pycache__?
  • Как написать код, который лучше всего использует кеш процессора для повышения производительности?
  • Как удалить кэш других приложений из нашего приложения для Android?
  • Отключение кеширования браузеров для всех браузеров из ASP.NET
  • Как очистить кеш gradleации?
  • Interesting Posts

    Сглаживание строк в Spark

    Ивритские письма, отображаемые в квадратах в еврейском шрифте в Chrome на Mac OS X 10.10.5 (Yosemite)

    Преобразование представления в bitmap на Android

    Outlook: ответьте как адрес электронной почты, сообщение было отправлено

    Нужно ли мне больше ОЗУ для программирования, если я переключусь на 64-битную ОС?

    Ошибки GParted на MBR Disk, утверждает, что это GPT. (Это ошибочно, но почему?)

    Неопределенный контейнер для хранения типично типизированных объектов в Java

    Условно начать в разных местах в раскадровке из AppDelegate

    Копирование установки Windows на другой HD

    Любой способ объявить размер / частичную границу поля?

    Ограничение UIDatePicker датируется конкретным временем. Например, вход DOB в ограниченный возрастной предел

    Как найти утечку памяти Java

    Как Windows 7 может запретить «нажатие мыши (тачпад) работать как клик»?

    В чем разница между публичным, частным, защищенным и не имеющим модификатора доступа?

    Как настроить фокусировку на JTextField?

    Давайте будем гением компьютера.