ASP.NET buforowanie MVC i IE - nieefektywne manipulowanie nagłówkami odpowiedzi

Tło

Próbuję pomóc koledze w debugowaniu problemu, który nie był problemem przez ostatnie 6 miesięcy. Po ostatnim wdrożeniu ASP.NET aplikacja MVC 2, FileResult odpowiedzi, które zmuszają użytkownika do otwarcia lub zapisania pliku PDF, mają problemy istniejące na tyle długo na komputerze klienckim, aby czytnik PDF mógł je otworzyć.

Wcześniejsze wersje IE (expecially 6) są jedynymi przeglądarkami, których to dotyczy. Firefox i Chrome oraz nowsze wersje IE (>8) zachowujcie się zgodnie z oczekiwaniami. Mając to na uwadze, następna sekcja określa działania niezbędne do odtworzenia problemu.

Zachowanie

  1. użytkownik klika link, który wskazuje na metodę akcji(zwykły hiperłącze z atrybutem href).
  2. metoda akcji generuje plik PDF reprezentowany jako strumień bajtów. Metoda zawsze odtwarza plik PDF.
  3. W metodzie action nagłówki są ustawione tak, aby instruowały przeglądarki, jak buforować odpowiedź. Oni są:

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

    Dla tych, którzy nie znają dokładnie tego, co robią nagłówki :

    A. Cache-Control: public

    Wskazuje, że odpowiedź może być buforowana przez dowolny bufor, nawet jeśli normalnie nie jest buforowalna lub buforowalna tylko wewnątrz nie-współdzielonego bufora.

    B. Cache-Control: must-revalidate

    Gdy dyrektywa must-revalidate jest obecna w odpowiedzi otrzymanej przez bufor, bufor ten nie może użyj wpisu, gdy stanie się nieświeży, aby odpowiedzieć na kolejne żądanie bez uprzedniej aktualizacji z serwerem origin

    C. Cache-Control: pre-check (wprowadzony z IE5)

    Definiuje interwał w sekundach, po którym jednostka musi być sprawdzona pod kątem świeżości. Sprawdzenie może się zdarzyć po wyświetleniu zasobu przez użytkownika, ale zapewnia, że w następnej podróży w obie strony buforowana kopia będzie aktualna.

    D. Cache-Control: post-check (wprowadzony z IE5)

    Definiuje interwał w sekundach, po którym jednostka musi być sprawdzona pod kątem świeżości przed pokazaniem użytkownikowi zasobu.

    E. Pragma: no-cache (aby zapewnić wsteczną kompatybilność z HTTP/1.0)

    Gdy dyrektywa no-cache jest obecna w wiadomości żądania, aplikacja powinna przesłać żądanie do serwera origin, nawet jeśli ma buforowaną kopię tego, co jest wymagane

    F. Wygasa

    Pole Expires entity-header podaje datę / czas, po którym odpowiedź jest uważana za przestarzałą.

  4. Zwracamy Plik z akcji

    return File(file, "mime/type", fileName);
    
  5. Użytkownik jest wyświetlany w oknie dialogowym Otwórz / Zapisz

  6. kliknięcie "Zapisz" działa zgodnie z oczekiwaniami, ale kliknięcie "Otwórz" uruchamia czytnik PDF, ale przechowywany plik tymczasowy IE został już usunięty do czasu reader próbuje otworzyć plik, więc skarży się, że plik nie istnieje (i jest).

Istnieje pół tuzina innych aplikacji, które używają tych samych nagłówków, aby wymusić Excel, CSV, PDF, Word i mnóstwo innych treści u użytkowników i nigdy nie było problemu.

Pytanie

  • czy nagłówki odpowiadają temu, co próbujemy zrobić? Chcemy, aby plik istniał tymczasowo( get cached), ale zawsze był zastępowany przez nowe wersje, nawet jeśli żądania mogą być identyczny).

Nagłówki odpowiedzi są ustawiane w metodzie action przed zwróceniem FileResult. Poprosiłem mojego kolegę, aby spróbował stworzyć nową klasę, która dziedziczy z FileResult i zamiast tego nadpisać metodę ExecuteResult, aby zmodyfikowała nagłówki, a następnie zrobiła base.ExecuteResult() zamiast -- brak statusu na tym.

Mam przeczucie, że winowajcą jest nagłówek" Expires "z" 0". Zgodnie z Ten artykuł W3C, ustawienie go na "0" oznacza "już wygasł."I do want it to be wygasło, po prostu nie chcę, aby IE usuwało go z systemu plików, zanim aplikacja obsługująca go będzie miała szansę go otworzyć.

Jak zawsze, dzięki!

Edit: Rozwiązanie

Po dalszych testach (Używając programu Fiddler do sprawdzenia nagłówków) zauważyliśmy, że nagłówki odpowiedzi, które myśleliśmy, że są ustawiane, nie były interpretowane przez przeglądarkę. Sam nie zaznajomiłem się z kodem, nie wiedziałem o podstawowym problemie: nagłówki były zdeptanie poza metodą działania.

/ Align = "left" / Wciąż nierozstrzygnięte jest to: wydaje się, że istnieje pewna rozbieżność między nagłówkiem Expires o wartości 0 a -1. Jeśli ktoś może z założenia rościć sobie pretensje do różnic , w odniesieniu do IE, to i tak chciałbym o tym usłyszeć. Jeśli chodzi o rozwiązanie, powyższe nagłówki działają zgodnie z przeznaczeniem z wartością Expires ustawioną na -1 we wszystkich przeglądarkach.

Update 1

The post jak kontrolować buforowanie stron internetowych we wszystkich przeglądarkach? opisuje szczegółowo, że buforowanie można zapobiec we wszystkich przeglądarkach za pomocą ustawienia Expires = 0. Nadal nie jestem przekonana o argumencie 0 vs -1...

Author: Community, 2012-02-10

3 answers

Myślę, że powinieneś użyć

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

Lub

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

Aby ustawić max-age=0, co nie oznacza nic więcej jako ponowna Walidacja pamięci podręcznej (patrz tutaj). Jeśli w nagłówku zostanie ustawiona dodatkowo ETag z pewną niestandardową sumą kontrolną hasha z danych, ETag z poprzedniego żądania zostanie wysłany na serwer. Serwer może zwrócić dane lub, w przypadku, gdy dane są dokładnie takie same jak wcześniej, może zwrócić puste ciało i HttpStatusCode.NotModified jako kod statusu. W takim przypadku przeglądarka internetowa pobierze dane z lokalnej pamięci podręcznej przeglądarki.

Zalecam użycie Cache-Control: private, które wymusza dwie ważne rzeczy: 1) Wyłącz buforowanie danych na serwerze proxy, który ma czasami bardzo agresywne ustawienia buforowania 2) pozwoli na buforowanie danych, ale nie pozwoli na dzielenie pamięci podręcznej z innymi użytkownikami. Może rozwiązać problemy z prywatnością, ponieważ dane, które zwracasz jednemu użytkownikowi, mogą nie być odczytywane przez innych użytkowników. Przy okazji kod HttpContext.Current.Response.Cache.SetMaxAge (new TimeSpan (0)) Ustaw Cache-Control: private, max-age=0 w nagłówku HTTP domyślnie. Jeśli chcesz użyć Cache-Control: public, możesz użyć SetCacheability (HttpCacheability.Public); do nadpisania zachowania lub użyć Headers.Set zamiast Cache.SetMaxAge.

Jeśli jesteś zainteresowany, aby przestudiować więcej opcji buforowania protokołu HTTP, polecam przeczytać buforowanie tutorial .

Aktualizacja : postanawiam napisać więcej informacji, aby oczyścić moje stanowisko. Odpowiada informacji Z Wikipedii nawet tak starych przeglądarek internetowych jak Mosaic 2.7, Netscape 2.0 i Internet Explorer 3.0 obsługuje marzec 1996, pre-standard HTTP/1.1 opisany w RFC 2068. Przypuszczam więc (ale nie testować), że stare przeglądarki internetowe obsługują nagłówek HTTP max-age=0. Netscape 2.06 i Internet Explorer 4.0 definitywnie wspierają HTTP 1.1.

Więc najpierw powinieneś zapytać: jakich standardów HTML używasz? Czy nadal używasz HTML 2.0 zamiast bardziej późnego HTML 3.2 opublikowanego w styczniu 1997? Przypuszczam, że używasz co najmniej HTML 4.0 opublikowanego w grudniu 1997. Więc jeśli zbudujesz swoją aplikację przynajmniej w HTML 4.0, Twoja strona może być zorientowana na klientach internetowych, które obsługują HTTP 1.1 i ignorować (nie wspierać) klientów internetowych, które nie obsługują HTTP 1.1.

Teraz o innych nagłówkach "Cache-Control "jako" private, max-age=0". W tym nagłówków jest moim zdaniem czysta paranoja. Ponieważ sam mam pewien problem z buforowaniem, próbowałem również dołączyć różne inne nagłówki, ale później po uważnym przeczytaniu sekcji 14.9 RFC2616 używam tylko "Cache-Control: private, max-age=0".

Jedynym nagłówkiem" Cache-Control", który może być dodatkowo omówiony, jest" must-rewalidate " opisany w sekcji 14.9.4, do której się wcześniej odwoływałem. Oto cytat:

Obowiązkowa nowelizacja dyrektywy jest niezbędna do wspierania rzetelnego obsługa niektórych funkcji protokołu. W każdych okolicznościach Pamięć podręczna HTTP/1.1 musi być zgodna z dyrektywą must-rewalidate; w szczególności, jeśli pamięć podręczna nie może dotrzeć do serwera origin dla z jakiegokolwiek powodu, musi wygenerować odpowiedź 504 (Gateway Timeout).

Serwery powinny wysyłać dyrektywę must-rewalidate wtedy i tylko wtedy, gdy niezastosowanie się do wniosku podmiotu może skutkować nieprawidłowe działanie, np. bezgłośne działanie finansowe transakcja. Odbiorcy nie mogą podejmować żadnych zautomatyzowanych działań, które narusza niniejszą dyrektywę i nie może automatycznie dostarczyć nieuwzględniona Kopia jednostki, jeżeli przedłużanie ważności nie powiedzie się.

Chociaż jest to niezalecane, agenci użytkownika pracujący w trudnych warunkach łączności ograniczenia mogą naruszać niniejszą dyrektywę, ale jeśli tak, muszą wyraźnie ostrzega użytkownika, że otrzymano nieujawnioną odpowiedź. Na przy każdym niezaliczonym dostępie należy podać Ostrzeżenie i należy wymagaj wyraźnego potwierdzenia użytkownika.

Czasami, jeśli mam problem z połączeniem internetowym, widzę pustą stronę z Komunikatem "Gateway Timeout". Wynika to z zastosowania dyrektywy "must-rewalidate". Nie wiem. pomyśl, że komunikat "Gateway Timeout" naprawdę pomaga użytkownikowi.

Więc osoby, jak wolą rozpocząć procedurę autodestrukcyjną, jeśli usłyszy sygnał " zajęty "podczas rozmowy z szefem, powinny dodatkowo użyć dyrektywy" must-rewalidate "w nagłówku" Cache-Control". Inne osoby polecam po prostu użyć "Cache-Control: private, max-age=0" i nic więcej.

 16
Author: Oleg,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-14 09:36:45

Wiem, że jest późno, ale ten link może być ogromną pomocą dla wszystkich zainteresowanych w temacie: http://dotnet.dzone.com/articles/output-caching-aspnet-mvc

 2
Author: Rosdi Kasim,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2013-05-14 17:47:50

Dla IE pamiętam, że musiałem ustawić Expires: -1. Jak zapobiec buforowaniu w Internet Explorer wydaje się to potwierdzać poniższym fragmentem kodu.

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

Patrząc wstecz w kodzie, oto co znalazłem. Ponadto, niejasno pamiętam, że jeśli ustawisz Cache-Control: private is może nie zachowywać się poprawnie z SSL.

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

Również, więc, nie chcesz buforować, co? wspomina -1, ale używa metod na Response.Cache zamiast:

// Stop Caching in IE
Response.Cache.SetCacheability(System.Web.HttpCacheability.NoCache);
// Stop Caching in Firefox
Response.Cache.SetNoStore();
Nie jest to jednak problem z buforowaniem strony ASP (IE8) mówi, że ten kod nie działa.
 0
Author: Kevin Hakanson,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2012-02-13 16:53:43