Também estávamos com esse bug, mas estávamos usando uma biblioteca de gerenciamento de ativos (Cassette). Após uma extensa investigação sobre esse problema, descobrimos que a causa raiz desse problema está na combinação de ASP.NET, IIS e Cassette. Não tenho certeza se esse é o seu problema (usando a HeadersAPI e não a CacheAPI), mas o padrão parece ser o mesmo.
Bug # 1
Cassette define o Vary: Accept-Encodingcabeçalho como parte de sua resposta a um pacote, pois pode codificar o conteúdo com gzip / deflate:
No entanto, o cache de saída do ASP.NET sempre retornará a resposta que foi armazenada em cache primeiro. Por exemplo, se a primeira solicitação tiver Accept-Encoding: gzipe Cassette retornar conteúdo compactado com gzip, o cache de saída do ASP.NET armazenará o URL em cache como Content-Encoding: gzip. A próxima solicitação para o mesmo URL, mas com uma codificação aceitável diferente (por exemplo Accept-Encoding: deflate), retornará a resposta em cache com Content-Encoding: gzip.
Esse bug é causado pelo Cassette usando a HttpResponseBase.CacheAPI para definir as configurações do cache de saída (por exemplo Cache-Control: public), mas usando a HttpResponseBase.HeadersAPI para definir o Vary: Accept-Encodingcabeçalho. O problema é que o ASP.NET nãoOutputCacheModule está ciente dos cabeçalhos de resposta; funciona apenas através da API. Ou seja, espera que o desenvolvedor use uma API invisivelmente acoplada em vez de apenas HTTP padrão.Cache
Bug # 2
Ao usar o IIS 7.5 (Windows Server 2008 R2), o bug nº 1 pode causar um problema separado nos caches do kernel e do usuário do IIS. Por exemplo, depois que um pacote é armazenado em cache com êxito Content-Encoding: gzip, é possível vê-lo no cache do kernel do IIS com netsh http show cachestate. Ele mostra uma resposta com 200 códigos de status e codificação de conteúdo de "gzip". Se a próxima solicitação tiver uma codificação aceitável diferente (por exemplo
Accept-Encoding: deflate) e um If-None-Matchcabeçalho que corresponda ao hash do pacote configurável, a solicitação nos caches do kernel e do modo de usuário do IIS será considerada um erro . Assim, fazendo com que o pedido seja tratado pelo Cassette, que retorna um 304:
No entanto, assim que o kernel e os modos de usuário do IIS processarem a resposta, eles verão que a resposta para a URL foi alterada e o cache deve ser atualizado. Se o cache do kernel do IIS for verificado netsh http show cachestatenovamente, a resposta em cache 200 será substituída por uma resposta 304. Todas as solicitações subsequentes ao pacote configurável, independentemente Accept-Encodinge If-None-Matchretornarão uma resposta 304. Vimos os efeitos devastadores desse bug em que todos os usuários receberam um 304 para nosso script principal por causa de uma solicitação aleatória que teve um inesperado Accept-Encodinge If-None-Match.
O problema parece ser que os caches do kernel do IIS e do modo de usuário não podem variar com base no Accept-Encodingcabeçalho. Como prova disso, usando a CacheAPI com a solução alternativa abaixo, os caches do kernel e do modo de usuário do IIS parecem sempre ser ignorados (apenas o cache de saída do ASP.NET é usado). Isso pode ser confirmado verificando se netsh http show cachestateestá vazio com a solução alternativa abaixo. O ASP.NET se comunica diretamente com o trabalhador do IIS para habilitar ou desabilitar seletivamente os kernel do IIS e os caches do modo de usuário por solicitação.
Não foi possível reproduzir esse bug nas versões mais recentes do IIS (por exemplo, IIS Express 10). No entanto, o bug nº 1 ainda era reproduzível.
Nossa correção original para esse bug foi desativar o cache do modo kernel / usuário do IIS apenas para solicitações de cassete, como as mencionadas anteriormente. Ao fazer isso, descobrimos o bug nº 1 ao implantar uma camada extra de cache na frente de nossos servidores da web. A razão que o hack string de consulta trabalhou é porque o OutputCacheModuleirá gravar um cache miss se a CacheAPI não foi usada para variar baseado no QueryString e se o pedido tem umQueryString .
Gambiarra
Planejamos nos afastar do Cassette de qualquer maneira. Portanto, em vez de manter nosso próprio fork do Cassette (ou tentar obter uma fusão de relações públicas), optamos por usar um módulo HTTP para solucionar esse problema.
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()
{
}
}
Espero que isso ajude alguém 😄!