Nous rencontrions également ce bogue, mais nous utilisions une bibliothèque de gestion des actifs (cassette). Après une enquête approfondie sur ce problème, nous avons constaté que la cause principale de ce problème est une combinaison d'ASP.NET, IIS et Cassette. Je ne suis pas sûr que ce soit votre problème (en utilisant l' Headers
API plutôt que l' Cache
API), mais le modèle semble être le même.
Bug # 1
Cassette définit l'en- Vary: Accept-Encoding
tête dans le cadre de sa réponse à un bundle car il peut encoder le contenu avec gzip / deflate:
Toutefois, le cache de sortie ASP.NET renvoie toujours la réponse qui a été mise en cache en premier. Par exemple, si la première demande a Accept-Encoding: gzip
et que Cassette renvoie du contenu compressé, le cache de sortie ASP.NET mettra l'URL en cache Content-Encoding: gzip
. La prochaine demande à la même URL mais avec un codage acceptable différent (par exemple Accept-Encoding: deflate
) renverra la réponse mise en cache avec Content-Encoding: gzip
.
Ce bogue est provoqué par Cassette utilisant l' HttpResponseBase.Cache
API pour définir les paramètres de cache de sortie (par exemple Cache-Control: public
) mais utilisant l' HttpResponseBase.Headers
API pour définir l'en- Vary: Accept-Encoding
tête. Le problème est que ASP.NET OutputCacheModule
n'est pas au courant des en-têtes de réponse; cela ne fonctionne que via l' Cache
API. Autrement dit, il s'attend à ce que le développeur utilise une API étroitement couplée de manière invisible plutôt qu'un simple HTTP standard.
Bug # 2
Lors de l'utilisation d'IIS 7.5 (Windows Server 2008 R2), le bogue n ° 1 peut provoquer un problème distinct avec le noyau IIS et les caches utilisateur. Par exemple, une fois qu'un bundle est correctement mis en cache avec Content-Encoding: gzip
, il est possible de le voir dans le cache du noyau IIS avec netsh http show cachestate
. Il affiche une réponse avec 200 codes d'état et un encodage de contenu "gzip". Si la requête suivante a un codage différent acceptable (par exemple
Accept-Encoding: deflate
) et un en- If-None-Match
tête qui correspond au hachage du paquet, la demande dans le noyau de IIS et les caches mode utilisateur sera considéré comme un manque . Ainsi, le traitement de la demande par Cassette qui renvoie un 304:
Cependant, une fois que le noyau et les modes utilisateur d'IIS auront traité la réponse, ils verront que la réponse pour l'URL a changé et que le cache doit être mis à jour. Si le cache du noyau IIS est vérifié à netsh http show cachestate
nouveau avec , la réponse 200 mise en cache est remplacée par une réponse 304. Toutes les demandes ultérieures au bundle, indépendamment de Accept-Encoding
et If-None-Match
renverront une réponse 304. Nous avons vu les effets dévastateurs de ce bogue où tous les utilisateurs ont reçu un 304 pour notre script principal en raison d'une demande aléatoire qui a eu un inattendu Accept-Encoding
et If-None-Match
.
Le problème semble être que le noyau IIS et les caches en mode utilisateur ne peuvent pas varier en fonction de l'en- Accept-Encoding
tête. Pour preuve, en utilisant l' Cache
API avec la solution de contournement ci-dessous, le noyau IIS et les caches en mode utilisateur semblent toujours être ignorés (seul le cache de sortie ASP.NET est utilisé). Cela peut être confirmé en vérifiant qu'il netsh http show cachestate
est vide avec la solution de contournement ci-dessous. ASP.NET communique directement avec le travailleur IIS pour activer ou désactiver sélectivement le noyau IIS et les caches en mode utilisateur par demande.
Nous n'avons pas pu reproduire ce bogue sur les versions plus récentes d'IIS (par exemple IIS Express 10). Cependant, le bogue n ° 1 était toujours reproductible.
Notre correctif d'origine pour ce bogue était de désactiver la mise en cache du mode utilisateur / noyau IIS uniquement pour les demandes de cassette comme d'autres mentionnées. Ce faisant, nous avons découvert le bogue n ° 1 lors du déploiement d'une couche supplémentaire de mise en cache devant nos serveurs Web. La raison pour laquelle le hack de chaîne de requête a fonctionné est que l' OutputCacheModule
enregistrera un cache manquant si l' Cache
API n'a pas été utilisée pour varier en fonction de QueryString
et si la demande a unQueryString
.
solution de contournement
Nous prévoyons de toute façon de nous éloigner de Cassette, donc plutôt que de maintenir notre propre fork de Cassette (ou d'essayer de fusionner un PR), nous avons choisi d'utiliser un module HTTP pour contourner ce problème.
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()
{
}
}
J'espère que cela aide quelqu'un 😄!