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, ти знайшов рішення ще? В іншому випадку дивіться мою редакцію, щоб ознайомитись з проблемою, і оформити нову відповідь нижче.
Ola Herrdahl

@OlaHerrdahl, ваш спосіб вирішення ідеальний :) У мене була і ця точна проблема. Дякую!
Ardee Aram

Відповіді:


3

Згідно з розділом 14.9 специфікації HTTP1.1 , no-cacheдиректива для заголовка кеша-керування неможлива лише сервером походження, а це означає, що 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.

EDIT:
На підставі вашого коментаря - так, я вважав, що ви не хочете, щоб файл був кешований. 304, таким чином, може з’являтися внаслідок того, що файл знаходиться в одному з внутрішніх кешів IIS. Погляньте на це:


Я думаю, ви тут неправильно зрозуміли мою проблему. Файл ще не знаходиться в моєму кеші. Але сервер відповідає 304 ... Це, здається, відбувається випадковим чином під час перегляду сайту у всіх основних браузерах.
Ola Herrdahl

@Ola Herrdahl: Подивіться на мою редакцію.
шквал

Я також спробував вимкнути кешування в IIS, і це не має ніякого значення ... :(
Ola Herrdahl

1

У мене виникає однакова проблема протягом деякого часу і всі кешування вимкнено ... Однак я встановив модуль стиснення для IIS7 в якийсь момент, який за замовчуванням дозволив стиснути статичні файли на моїх існуючих сайтах. Я вимкнув усе стиснення для пошкоджених ділянок і тепер, здається, вони працюють на тонкому дереві .


1

Ми також відчували цю помилку, але ми використовували бібліотеку управління активами (касету). Після широкого дослідження цього питання ми з’ясували, що першопричиною цієї проблеми є поєднання ASP.NET, IIS та Cassette. Я не впевнений, чи це ваша проблема (використовуючи HeadersAPI, а не CacheAPI), але модель, здається, однакова.

Помилка №1

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

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

Ця помилка викликана Касетою, що використовує 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()
    {

    }
}

Я сподіваюся, що це комусь допоможе 😄!

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.