IIS 7 возвращает 304 вместо 200

У меня странная проблема с IIS 7.
Иногда кажется, что возвращается 304 вместо 200.

Вот пример запроса, полученного с помощью Fiddler:
(Обратите внимание, что запрошенный файл еще не находится в кэше моего браузера.)

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: ...

Обратите внимание, что в запросе нет If-Modified-Since или If-None-Match.
Но все же ответ таков:

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

Кто-нибудь знает, что здесь может быть не так?

Я использую IIS 7 на Windows Web Server 2008 R2.

РЕДАКТИРОВАТЬ:

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

<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>

3 ответа

Согласно разделу 14.9 спецификации HTTP1.1, no-cache директива для заголовка Cache-Control только навязывается исходным сервером, что означает, что IIS игнорирует заголовок в вашем запросе.

Директивы управления кэшем можно разбить на следующие общие категории:

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

сервером происхождения.

Раздел 14.9.1 определяет public, private, а также no-cache в качестве директив, ограничивающих то, что кешируется, что может быть наложено только сервером.

Если вы не хотите, чтобы ваш файл.js кэшировался, вам нужно либо установить no-cache директива в приложении (т. е. код ASP.NET), или вам нужно изменить Cache-Control заголовок в запросе на использование no-store директива вместо no-cache,

РЕДАКТИРОВАТЬ:
На основании вашего комментария - да, я предположил, что вы не хотите, чтобы файл кэшировался. Таким образом, 304 может появиться в результате того, что файл находится в одном из внутренних кэшей IIS. Посмотрите на это:

Некоторое время у меня была такая же проблема, и все кэширование отключено... Однако в какой-то момент я установил модуль сжатия для IIS7, который по умолчанию включил сжатие статических файлов на моих существующих сайтах. Я отключил все сжатие для затронутых участков, и теперь они, кажется, работают нормально на ощупь.

Мы также сталкивались с этой ошибкой, но мы использовали библиотеку управления активами (кассету). После тщательного изучения этой проблемы мы обнаружили, что основной причиной этой проблемы является сочетание ASP.NET, IIS и кассеты. Я не уверен, что это ваша проблема (используя Headers API, а не Cache API), но картина, кажется, такая же.

Ошибка № 1

Кассета устанавливает Vary: Accept-Encoding заголовок как часть его ответа на пакет, так как он может кодировать содержимое с помощью gzip / deflate:

Однако кэш вывода ASP.NET всегда будет возвращать ответ, который был кэширован первым. Например, если первый запрос имеет Accept-Encoding: gzip и кассета возвращает сжатый контент, выходной кеш ASP.NET будет кэшировать URL как Content-Encoding: gzip, Следующий запрос к тому же URL, но с другой приемлемой кодировкой (например, Accept-Encoding: deflate) вернет кэшированный ответ с Content-Encoding: gzip,

Эта ошибка вызвана использованием кассеты HttpResponseBase.Cache API для настройки параметров кэша вывода (например, Cache-Control: public) но используя HttpResponseBase.Headers API для установки Vary: Accept-Encoding заголовок. Проблема в том, что ASP.NET OutputCacheModule не знает заголовков ответа; это работает только через Cache API. То есть он ожидает, что разработчик будет использовать невидимо тесно связанный API, а не только стандартный HTTP.

Ошибка № 2

При использовании IIS 7.5 (Windows Server 2008 R2) ошибка # 1 может вызвать отдельную проблему с ядром IIS и пользовательским кэшем. Например, после успешного кэширования пакета Content-Encoding: gzip, это можно увидеть в кеше ядра IIS с netsh http show cachestate, Он показывает ответ с 200 кодами состояния и кодировкой содержимого "gzip". Если следующий запрос имеет другую приемлемую кодировку (например, Accept-Encoding: deflate) и If-None-Match заголовок, соответствующий хешу пакета, запрос к кешу ядра IIS и пользовательского режима будет считаться пропуском. Таким образом, вызывая обработку запроса кассетой, которая возвращает 304:

Однако, как только ядро ​​и пользовательские режимы IIS обработают ответ, они увидят, что ответ для URL изменился, и кэш должен быть обновлен. Если кеш ядра IIS проверен с netsh http show cachestate снова, кэшированный ответ 200 заменяется ответом 304. Все последующие запросы к пакету, независимо от Accept-Encoding а также If-None-Match вернет ответ 304. Мы увидели разрушительные последствия этой ошибки, когда все пользователи получили 304 для нашего основного скрипта из-за случайного запроса, который имел неожиданный Accept-Encoding а также If-None-Match,

Кажется, что проблема заключается в том, что кэши ядра и пользовательского режима IIS не могут изменяться в зависимости от Accept-Encoding заголовок. В качестве доказательства этого, используя Cache API с обходным путем ниже, кэши ядра IIS и пользовательского режима, кажется, всегда пропускаются (используется только кэш вывода ASP.NET). Это можно подтвердить, проверив, что netsh http show cachestate пуст с обходным путем ниже. ASP.NET обменивается данными с работником IIS напрямую, чтобы выборочно включать или отключать ядро ​​IIS и кэши пользовательского режима для каждого запроса.

Мы не смогли воспроизвести эту ошибку на более новых версиях IIS (например, IIS Express 10). Тем не менее, ошибка № 1 была все еще воспроизводимой.

Наше первоначальное исправление этой ошибки состояло в том, чтобы отключить кэширование в ядре / пользовательском режиме IIS только для запросов на кассету, как и другие упомянутые выше. Таким образом, мы обнаружили ошибку №1 при развертывании дополнительного уровня кэширования перед нашими веб-серверами. Причина того, что хак строки запроса сработал, заключается в том, что OutputCacheModule запишет пропущенный кеш, если Cache API не использовался для изменения в зависимости от QueryString и если запрос имеет QueryString,

Временное решение

Мы планировали в любом случае отойти от Кассеты, поэтому вместо того, чтобы поддерживать наш собственный форк Кассеты (или пытаться объединить PR), мы решили использовать модуль HTTP, чтобы обойти эту проблему.

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()
    {

    }
}

Я надеюсь, что это помогает кому-то!

Другие вопросы по тегам