IIS 7 zwraca 304 zamiast 200


10

Mam dziwny problem z IIS 7.
Czasami wydaje się, że zwraca 304 zamiast 200.

Oto przykładowe żądanie przechwycone przez Fiddler:
(Zauważ, że żądany plik nie znajduje się jeszcze w pamięci podręcznej przeglądarki).

GET https://[mysite]/Content/js/jquery.form.js HTTP/1.1
Accept: */*
Referer: https://[mysite]/Welcome/News
Accept-Language: sv-SE
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; InfoPath.2; OfficeLiveConnector.1.4; OfficeLivePatch.1.3; .NET4.0C; .NET4.0E)
Accept-Encoding: gzip, deflate
Host: [mysite]
Connection: Keep-Alive
Cache-Control: no-cache
Cookie: ...

Zauważ, że w żądaniu nie ma żadnych elementów If-Modified-Since lub If-None-Match.
Ale nadal odpowiedź brzmi:

HTTP/1.1 304 Not Modified
Cache-Control: public
Expires: Tue, 02 Mar 2010 06:26:08 GMT
Last-Modified: Mon, 22 Feb 2010 21:58:44 GMT
ETag: "1CAB40A337D4200"
Server: Microsoft-IIS/7.5
X-Powered-By: ASP.NET
Date: Mon, 01 Mar 2010 17:06:34 GMT

Czy ktoś ma pojęcie, co może być tutaj nie tak?

Używam IIS 7 na Windows Web Server 2008 R2.

EDYTOWAĆ:

Znalazłem obejście, włączyłem buforowanie, a następnie wyłączyłem je na poziomie rozszerzenia.

<configuration>
  <system.webServer>
    <caching enabled="true" enableKernelCache="true">
      <profiles>
        <add extension=".png" policy="DisableCache" kernelCachePolicy="DisableCache" />
        <add extension=".gif" policy="DisableCache" kernelCachePolicy="DisableCache" />
        <add extension=".js" policy="DisableCache" kernelCachePolicy="DisableCache" />
        <add extension=".css" policy="DisableCache" kernelCachePolicy="DisableCache" />
      </profiles>
    </caching>
    <staticContent>
      <clientCache cacheControlMode="NoControl" />
    </staticContent>
  </system.webServer>
</configuration>

Mam DOKŁADNY ten sam problem, który powoduje wiele problemów na bardzo obciążonym serwerze internetowym. Usługi IIS zwracają wartość 304, mimo że klient nie ma jeszcze kopii zasobu.
Philippe Leybaert

@Filippe, znalazłeś już rozwiązanie? W przeciwnym razie zapoznaj się z moją edycją powyżej w celu obejścia tego problemu i sprawdź nową odpowiedź poniżej.
Ola Herrdahl

@OlaHerrdahl, twoje obejście jest idealne :) Też miałem dokładnie ten problem. Dzięki!
Ardee Aram

Odpowiedzi:


3

Według sekcji 14,9 z HTTP1.1 Spec The no-cachedyrektywa dla nagłówka Cache-Control jest imposable tylko przez serwer pochodzenia, co oznacza IIS ignoruje nagłówka w swoim wniosku.

Dyrektywy dotyczące kontroli pamięci podręcznej można podzielić na następujące ogólne kategorie:

  - Restrictions on what are cacheable; these may only be imposed

przez serwer pochodzenia.

Sekcja 14.9.1 definiuje public, privatei no-cachejako dyrektyw ograniczających co jest buforowalny, które mogą być nakładane jedynie przez serwer.

Jeśli nie chcesz buforować pliku .js, musisz ustawić no-cachedyrektywę w aplikacji (tj. Kod ASP.NET) lub zmienić Cache-Controlnagłówek w żądaniu, aby użyć no-storedyrektywy zamiast no-cache.

EDYCJA:
Na podstawie twojego komentarza - tak, zakładam, że nie chcesz buforować pliku. Zatem 304 może pochodzić z powodu umieszczenia pliku w jednej z wewnętrznych pamięci podręcznych IIS. Spójrz na te:


Myślę, że źle zrozumiałeś mój problem. Plik nie znajduje się jeszcze w mojej pamięci podręcznej. Ale serwer odpowiada 304 ... Wydaje się, że dzieje się to losowo podczas przeglądania strony we wszystkich głównych przeglądarkach.
Ola Herrdahl

@Ola Herrdahl: Zobacz moją edycję.
squillman

Próbowałem również wyłączyć buforowanie w IIS, a to nie robi różnicy ... :(
Ola Herrdahl

1

Od jakiegoś czasu mam ten sam problem i wyłączam buforowanie ... Jednak w pewnym momencie zainstalowałem moduł kompresji dla IIS7, który domyślnie włączył kompresję plików statycznych na moich istniejących stronach. Wyłączyłem całą kompresję dla dotkniętych stron, a teraz wydaje się, że działają dobrze w dotyku drewna .


1

Ten błąd również występował, ale korzystaliśmy z biblioteki zarządzania zasobami (kasety). Po dogłębnym zbadaniu tego problemu stwierdziliśmy, że główną przyczyną tego problemu jest kombinacja ASP.NET, IIS i kasety. Nie jestem pewien, czy to jest twój problem (używanie Headersinterfejsu API zamiast Cacheinterfejsu API), ale wzorzec wydaje się być taki sam.

Błąd nr 1

Cassette ustawia Vary: Accept-Encodingnagłówek jako część swojej odpowiedzi na pakiet, ponieważ może kodować zawartość za pomocą gzip / deflate:

Jednak wyjściowa pamięć podręczna ASP.NET zawsze zwróci odpowiedź, która była najpierw buforowana. Na przykład, jeśli pierwsze żądanie ma, Accept-Encoding: gzipa kaseta zwraca zawartość spakowaną gzip, wyjściowa pamięć podręczna ASP.NET buforuje adres URL jako Content-Encoding: gzip. Następne żądanie do tego samego adresu URL, ale z innym dopuszczalnym kodowaniem (np. Accept-Encoding: deflate), Zwróci buforowaną odpowiedź za pomocą Content-Encoding: gzip.

Ten błąd jest spowodowany przez ustawienie kasety przy użyciu HttpResponseBase.Cacheinterfejsu API do ustawienia wyjściowej pamięci podręcznej (np. Cache-Control: public), Ale użycie HttpResponseBase.Headersinterfejsu API do ustawienia Vary: Accept-Encodingnagłówka. Problemem jest to, że ASP.NET OutputCacheModulejest nie świadomy nagłówków odpowiedzi; działa tylko poprzez CacheAPI. Oznacza to, że oczekuje od programisty użycia niewidocznie ściśle powiązanego interfejsu API, a nie tylko standardowego protokołu HTTP.

Błąd nr 2

Podczas korzystania z IIS 7.5 (Windows Server 2008 R2) błąd nr 1 może powodować osobny problem z jądrem IIS i pamięciami podręcznymi użytkowników. Na przykład po udanym buforowaniu pakietu Content-Encoding: gzipmożna go zobaczyć w pamięci podręcznej jądra IIS za pomocą netsh http show cachestate. Pokazuje odpowiedź z kodem stanu 200 i kodowaniem zawartości „gzip”. Jeśli kolejna prośba ma inną dopuszczalną kodowania (np Accept-Encoding: deflate) i jest If-None-Matchnagłówek, który pasuje do mieszania wiązki, tym prośba do jądra i trybu użytkownika skrytek IIS będą uważane za chybienie . W ten sposób powodując, że żądanie jest obsługiwane przez kasetę, która zwraca 304:

Jednak gdy jądro i tryby użytkownika IIS przetworzą odpowiedź, zobaczą, że odpowiedź na adres URL uległa zmianie i pamięć podręczna powinna zostać zaktualizowana. Jeśli pamięć podręczna jądra IIS zostanie netsh http show cachestateponownie zaznaczona , odpowiedź w pamięci podręcznej 200 zostanie zastąpiona odpowiedzią 304. Wszystkie kolejne żądania do pakietu, niezależnie od tego Accept-Encodingi If-None-Matchzwracają odpowiedź 304. Widzieliśmy druzgocące skutki tego błędu, w którym wszyscy użytkownicy otrzymali 304 dla naszego głównego skryptu z powodu losowego żądania, które miało nieoczekiwane Accept-Encodingi If-None-Match.

Problem polega na tym, że jądra usług IIS i pamięci podręczne trybu użytkownika nie mogą się różnić w zależności od Accept-Encodingnagłówka. Jako dowód na to, używając Cacheinterfejsu API z poniższym obejściem, wydaje się, że pamięci podręczne jądra IIS i trybu użytkownika są zawsze pomijane (używana jest tylko wyjściowa pamięć podręczna ASP.NET). Można to potwierdzić, sprawdzając, czy pole netsh http show cachestatejest puste, korzystając z poniższego obejścia. Program ASP.NET komunikuje się bezpośrednio z procesorem roboczym IIS w celu selektywnego włączania lub wyłączania pamięci podręcznej jądra IIS i trybu użytkownika na żądanie.

Nie byliśmy w stanie odtworzyć tego błędu w nowszych wersjach IIS (np. IIS Express 10). Jednak błąd nr 1 był nadal powtarzalny.

Naszym oryginalnym rozwiązaniem tego błędu było wyłączenie buforowania trybu jądra / trybu użytkownika IIS tylko dla żądań kasety, jak wspomniano inni. W ten sposób odkryliśmy błąd nr 1 podczas wdrażania dodatkowej warstwy buforowania przed naszymi serwerami WWW. Powodem, że siekać ciąg kwerendy pracował dlatego, że OutputCacheModulenagra miss cache jeśli CacheAPI nie został wykorzystany do zależą od QueryString a jeżeli wniosek maQueryString .

Obejście

W każdym razie planowaliśmy odejść od Cassette, więc zamiast utrzymywać własne rozwidlenie kasety (lub próbować scalić PR), zdecydowaliśmy się użyć modułu HTTP do obejścia tego problemu.

public class FixCassetteContentEncodingOutputCacheBugModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.PostRequestHandlerExecute += Context_PostRequestHandlerExecute;
    }

    private void Context_PostRequestHandlerExecute(object sender, EventArgs e)
    {
        var httpContext = HttpContext.Current;

        if (httpContext == null)
        {
            return;
        }

        var request = httpContext.Request;
        var response = httpContext.Response;

        if (request.HttpMethod != "GET")
        {
            return;
        }

        var path = request.Path;

        if (!path.StartsWith("/cassette.axd", StringComparison.InvariantCultureIgnoreCase))
        {
            return;
        }

        if (response.Headers["Vary"] == "Accept-Encoding")
        {
            httpContext.Response.Cache.VaryByHeaders.SetHeaders(new[] { "Accept-Encoding" });
        }
    }

    public void Dispose()
    {

    }
}

Mam nadzieję, że to komuś pomoże 😄!

Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.