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

10

У меня странная проблема с 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>
Ола Херрдал
источник
У меня точно такая же проблема, и она вызывает много проблем на очень загруженном веб-сервере. IIS возвращает 304, даже если у клиента еще нет копии ресурса.
Филипп Лейберт
@Philippe, ты уже нашел решение? В противном случае см. Мои правки выше для обхода проблемы и ознакомьтесь с новым ответом ниже.
Ола Херрдал
@OlaHerrdahl, твой обходной путь идеален :) У меня тоже была эта проблема. Спасибо!
Арди Арам

Ответы:

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. Посмотрите на это:

squillman
источник
Я думаю, что вы неправильно поняли мою проблему здесь. Файл еще не находится в моем кеше. Но все равно сервер отвечает с 304 ... Это происходит случайно при просмотре сайта во всех основных браузерах.
Ола Херрдал
@ Ола Херрдал: Взгляни на мою редакцию.
squillman
Я попытался отключить кэширование в IIS, и это не имеет значения ... :(
Ола Херрдал
1

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


источник
1

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

Ошибка № 1

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

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

Эта ошибка вызвана тем, что Cassette использует HttpResponseBase.CacheAPI для установки параметров кэша вывода (например Cache-Control: public), но использует HttpResponseBase.HeadersAPI для установки Vary: Accept-Encodingзаголовка. Проблема заключается в том, что ASP.NET OutputCacheModuleявляется не знают заголовки ответа; это работает только через CacheAPI. То есть он ожидает, что разработчик будет использовать невидимо тесно связанный 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заголовка. Как доказательство этого, при использовании CacheAPI с обходным решением ниже кэши ядра IIS и пользовательского режима, похоже, всегда пропускаются (используется только кэш вывода ASP.NET). Это можно подтвердить, проверив, что netsh http show cachestateпусто с обходным путем ниже. ASP.NET взаимодействует с работником IIS напрямую, чтобы выборочно включать или отключать ядро ​​IIS и кэши пользовательского режима для каждого запроса.

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

Наше первоначальное исправление этой ошибки заключалось в том, чтобы отключить кэширование в ядре / пользовательском режиме IIS только для запросов на кассету, как это уже упоминалось. Таким образом, мы обнаружили ошибку №1 при развертывании дополнительного уровня кэширования перед нашими веб-серверами. Причина того, что хак строки запроса сработал, заключается в том, что он OutputCacheModuleбудет регистрировать пропадание кэша, если CacheAPI не использовался для изменения в зависимости от 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()
    {

    }
}

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

TheCloudlessSky
источник