Мы также сталкивались с этой ошибкой, но мы использовали библиотеку управления активами (кассету). После тщательного изучения этой проблемы мы обнаружили, что основной причиной этой проблемы является сочетание 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()
{
}
}
Я надеюсь, что это помогает кому-то 😄!