Фон
Я пытаюсь помочь коллеге отладить проблему, которая не была проблемой в течение последних 6 месяцев. После последнего развертывания приложения ASP.NET MVC 2 ответы FileResult
, которые заставляют PDF файл у пользователя открывать или сохранять, имеют проблемы, которые достаточно долго сохраняются на клиентской машине для чтения PDF файлов, чтобы открыть их.
Более ранние версии IE (expecially 6) являются единственными затронутыми браузерами. Firefox и Chrome и более новые версии IE ( > 8) ведут себя так, как ожидалось. Имея это в виду, следующий раздел определяет действия, необходимые для воссоздания проблемы.
Поведение
- Пользователь нажимает ссылку, указывающую на метод действия (простая гиперссылка с атрибутом
href
). - Метод действия генерирует PDF, представляемый как поток байтов. Метод всегда воссоздает PDF.
-
В методе действий заголовки настроены на указание браузерам, как кэшировать ответ. Это:
response.AddHeader("Cache-Control", "public, must-revalidate, post-check=0, pre-check=0"); response.AddHeader("Pragma", "no-cache"); response.AddHeader("Expires", "0");
Для тех, кто не знаком с тем, что делают заголовки:
а. Cache-Control: public
Указывает, что ответ может быть кэширован любым кешем, даже если он обычно не кэшируется или кэшируется только в кэше, не использующем общий доступ.
б. Cache-Control: must-revalidate
Когда директива must-revalidate присутствует в ответе, полученном кешем, этот кеш НЕ ДОЛЖЕН использовать запись после того, как она станет устаревшей, чтобы ответить на последующий запрос без предварительной проверки его с сервером происхождения
с. Cache-Control: предварительная проверка (представлена с IE5)
Определяет интервал в секундах, после которого объект должен быть проверен на свежесть. Проверка может произойти после того, как пользователю будет показан ресурс, но гарантирует, что в следующем обратном просмотре кешированная копия будет актуальной.
д. Cache-Control: post-check (введен с IE5)
Определяет интервал в секундах, после которого объект должен быть проверен на свежесть, прежде чем показывать пользователю ресурс.
е. Pragma: no-cache (для обеспечения обратной совместимости с HTTP/1.0)
Когда директива no-cache присутствует в сообщении-запросе, приложение ДОЛЖНО пересылать запрос на исходный сервер, даже если у него есть кэшированная копия того, что запрашивается.
ф. Истекает
В поле Expires entity-header указывается дата/время, после которого ответ считается устаревшим.
-
Мы возвращаем файл из действия
return File(file, "mime/type", fileName);
-
Пользователь получает диалоговое окно "Открыть/Сохранить"
- Нажатие кнопки "Сохранить" работает так, как ожидалось, но нажатие "Открыть" запускает PDF-ридер, но временный файл IE, который был сохранен, уже был удален к тому моменту, когда читатель пытается открыть файл, поэтому он жалуется, что файл отсутствует (и это так).
Здесь есть еще полдюжины других приложений, которые используют одни и те же заголовки, чтобы заставить Excel, CSV, PDF, Word и тонну другого контента у пользователей, и никогда не возникало проблем.
Вопрос
- Правильны ли заголовки для того, что мы пытаемся сделать? Мы хотим, чтобы файл существовал временно (получить кеширование), но всегда должен быть заменен новыми версиями, хотя запросы могут быть идентичными).
Заголовки ответа устанавливаются в методе действий перед возвратом a FileResult
. Я попросил своего коллегу попытаться создать новый класс, который наследует от FileResult
, и вместо этого переопределить метод ExecuteResult
, чтобы он изменял заголовки, а затем вместо него base.ExecuteResult()
- никакого статуса.
У меня есть подозрение, что заголовок "Истекает" "0" является виновником. Согласно этой статье W3C, установка его в "0" означает "уже истек". Я действительно хочу, чтобы он истек, я просто не хочу, чтобы IE удалял его из файловой системы до того, как приложение обработало его, и он может открыть его.
Как всегда, спасибо!
Изменить: решение
При дальнейшем тестировании (используя Fiddler для проверки заголовков) мы видели, что заголовки ответов, которые, как мы думали, устанавливались, не были интерпретированы браузером. Не знакомый с кодом сам, я не знал об основной проблеме: заголовки попадали вне метода действий.
Тем не менее, я собираюсь оставить этот вопрос открытым. И все-таки выдающийся это: похоже, существует некоторая разница между заголовком Expires
, имеющим значение 0
vs. -1
. Если кто-то может претендовать на различия по дизайну, то в отношении IE я все равно хотел бы услышать об этом. Что касается решения, то вышеупомянутые заголовки работают так, как предполагалось, с значением Expires
, установленным на -1
во всех браузерах.
Обновление 1
Сообщение Как контролировать кэширование веб-страниц во всех браузерах? подробно описывает, что кэширование можно предотвратить во всех браузерах с помощью установки Expires = 0. Я все еще не продаюсь в этом аргументе 0
vs -1
...