Stavamo riscontrando anche questo errore ma stavamo usando una libreria di gestione patrimoniale (Cassette). Dopo un'indagine approfondita di questo problema, abbiamo scoperto che la causa principale di questo problema è con una combinazione di ASP.NET, IIS e Cassette. Non sono sicuro che questo sia il tuo problema (utilizzando l' Headers
API anziché l' Cache
API), ma il modello sembra essere lo stesso.
Bug n. 1
Cassette imposta l' Vary: Accept-Encoding
intestazione come parte della sua risposta a un bundle poiché può codificare il contenuto con gzip / deflate:
Tuttavia, la cache di output ASP.NET restituirà sempre la risposta memorizzata per prima. Ad esempio, se la prima richiesta ha Accept-Encoding: gzip
e Cassette restituisce contenuto compresso con gzip, la cache di output ASP.NET memorizzerà nella cache l'URL come Content-Encoding: gzip
. La richiesta successiva allo stesso URL ma con una codifica accettabile diversa (ad es. Accept-Encoding: deflate
) Restituirà la risposta memorizzata nella cache con Content-Encoding: gzip
.
Questo errore è causato da Cassette che utilizza l' HttpResponseBase.Cache
API per impostare le impostazioni della cache di output (ad es. Cache-Control: public
) Ma che utilizza l' HttpResponseBase.Headers
API per impostare l' Vary: Accept-Encoding
intestazione. Il problema è che ASP.NET nonOutputCacheModule
è a conoscenza delle intestazioni di risposta; funziona solo tramite l' Cache
API. Cioè, si aspetta che lo sviluppatore utilizzi un'API invisibilmente accoppiata anziché solo HTTP standard.
Bug n. 2
Quando si utilizza IIS 7.5 (Windows Server 2008 R2), il bug n. 1 può causare un problema separato con il kernel IIS e le cache degli utenti. Ad esempio, una volta che un bundle viene correttamente memorizzato nella cache Content-Encoding: gzip
, è possibile vederlo nella cache del kernel IIS con netsh http show cachestate
. Mostra una risposta con 200 codici di stato e codifica del contenuto di "gzip". Se la richiesta successiva ha una codifica differente accettabile (ad esempio
Accept-Encoding: deflate
) e un If-None-Match
un'intestazione che corrisponda hash del fascio, la richiesta in modalità kernel e utente cache di IIS sarà considerato un perdere . Pertanto, facendo sì che la richiesta venga gestita da Cassette che restituisce un 304:
Tuttavia, una volta che il kernel di IIS e le modalità utente elaborano la risposta, vedranno che la risposta per l'URL è cambiata e la cache deve essere aggiornata. Se la cache del kernel IIS viene netsh http show cachestate
nuovamente verificata , la risposta 200 memorizzata nella cache viene sostituita con una risposta 304. Tutte le successive richieste al bundle, indipendentemente da Accept-Encoding
e If-None-Match
restituiranno una risposta 304. Abbiamo visto gli effetti devastanti di questo bug in cui a tutti gli utenti è stato fornito un 304 per il nostro script principale a causa di una richiesta casuale che ha avuto un imprevisto Accept-Encoding
e If-None-Match
.
Il problema sembra essere che il kernel IIS e le cache in modalità utente non sono in grado di variare in base all'intestazione Accept-Encoding
. A riprova di ciò, usando l' Cache
API con la soluzione seguente, il kernel IIS e le cache in modalità utente sembrano essere sempre ignorate (viene utilizzata solo la cache di output ASP.NET). Ciò può essere confermato controllando che netsh http show cachestate
sia vuoto con la soluzione alternativa di seguito. ASP.NET comunica direttamente con il lavoratore IIS per abilitare o disabilitare selettivamente il kernel IIS e le cache in modalità utente per richiesta.
Non è stato possibile riprodurre questo errore nelle versioni più recenti di IIS (ad esempio IIS Express 10). Tuttavia, il bug n. 1 era ancora riproducibile.
La nostra correzione originale per questo bug era disabilitare la cache in modalità utente / kernel IIS solo per richieste Cassette come altre citate. In questo modo, abbiamo scoperto il bug n. 1 durante la distribuzione di un ulteriore livello di cache davanti ai nostri server web. Il motivo per cui l'hacking della stringa di query ha funzionato è perché OutputCacheModule
registrerà un errore nella cache se l' Cache
API non è stata utilizzata per variare in base al QueryString
e se la richiesta ha unQueryString
.
Soluzione
Abbiamo comunque pianificato di allontanarci da Cassette, quindi piuttosto che mantenere il nostro fork di Cassette (o cercare di unire le PR), abbiamo deciso di utilizzare un modulo HTTP per aggirare questo 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()
{
}
}
Spero che questo aiuti qualcuno 😄!