Biz de bu hatayı yaşıyorduk ama bir varlık yönetimi kütüphanesi (Kaset) kullanıyorduk. Bu sorunu kapsamlı bir şekilde inceledikten sonra, bu sorunun temel nedeninin ASP.NET, IIS ve Kaset birleşiminden kaynaklandığını tespit ettik. Bu olup olmadığından emin değilim senin (kullanarak sorunu Headers
yerine API Cache
API), ancak deseni aynı gibi görünüyor.
Hata # 1
Kaset Vary: Accept-Encoding
, içeriği gzip / deflate ile kodlayabileceğinden, bir pakete verdiği yanıtın bir parçası olarak ayarlar :
Ancak, ASP.NET çıktı önbelleği her zaman ilk olarak önbelleğe alınan yanıtı döndürür. Örneğin, ilk istek varsa Accept-Encoding: gzip
ve Kaset gzip edilmiş içeriği döndürürse, ASP.NET çıktı önbelleği URL'yi önbelleğe alır Content-Encoding: gzip
. Aynı URL'ye ancak farklı bir kabul edilebilir kodlamaya (ör. Accept-Encoding: deflate
) Yönelik bir sonraki istek , önbelleğe alınan yanıtı döndürür Content-Encoding: gzip
.
Bu hataya, HttpResponseBase.Cache
çıkış önbelleği ayarlarını ayarlamak için API'yi kullanan kaset neden olur (ör. Cache-Control: public
), Ancak başlığı HttpResponseBase.Headers
ayarlamak için API'yi kullanır Vary: Accept-Encoding
. Sorun ASP.NET olmasıdır OutputCacheModule
olduğu değil yanıt başlıkları farkında; yalnızca Cache
API üzerinden çalışır . Yani, geliştiricinin standart HTTP yerine görünmez sıkı sıkıya bağlı bir API kullanmasını bekler.
Hata # 2
IIS 7.5 (Windows Server 2008 R2) kullanırken, hata # 1, IIS çekirdeği ve kullanıcı önbelleklerinde ayrı bir soruna neden olabilir. Örneğin, bir paket başarıyla önbelleğe alındıktan sonra, dizini Content-Encoding: gzip
IIS çekirdek önbelleğinde görmek mümkündür netsh http show cachestate
. 200 durum kodu ve "gzip" içerik kodlaması ile bir yanıt gösterir. Sonraki istekte farklı bir kabul edilebilir kodlama (ör.
Accept-Encoding: deflate
) VeIf-None-Match
paketin karmasıyla eşleşen bir başlık varsa, IIS'nin çekirdeği ve kullanıcı modu önbelleklerine yönelik istek bir özledim olarak kabul edilir . Böylece, talebin 304 döndüren Cassette tarafından işlenmesine neden olur:
Ancak, IIS'nin çekirdek ve kullanıcı modları yanıtı işlediğinde, URL'nin yanıtının değiştiğini ve önbelleğin güncelleştirilmesi gerektiğini görürler. IIS çekirdek önbelleği netsh http show cachestate
yeniden işaretlenirse , önbelleğe alınan 200 yanıtı 304 yanıtı ile değiştirilir. Daha sonra gelen tüm istekler, 304 yanıtından bağımsız olarak Accept-Encoding
ve If-None-Match
geri dönecektir. Beklenmedik Accept-Encoding
ve rastgele bir istek nedeniyle tüm kullanıcılara temel komut dosyamız için bir 304 sunulduğu bu hatanın yıkıcı etkilerini gördük If-None-Match
.
Sorun, IIS çekirdeği ve kullanıcı modu önbelleklerinin Accept-Encoding
başlığa göre değişememesi gibi görünüyor . Bunun kanıtı olarak, Cache
API'yi aşağıdaki geçici çözümle kullanarak IIS çekirdeği ve kullanıcı modu önbellekleri her zaman atlanmış gibi görünür (yalnızca ASP.NET çıkış önbelleği kullanılır). Bu, aşağıdaki çözümle netsh http show cachestate
boş olup olmadığını kontrol ederek doğrulanabilir . ASP.NET, isteğe bağlı olarak IIS çekirdeği ve kullanıcı modu önbelleklerini seçerek etkinleştirmek veya devre dışı bırakmak için IIS çalışanıyla doğrudan iletişim kurar.
Bu hatayı IIS'nin yeni sürümlerinde (ör. IIS Express 10) yeniden üretemedik. Ancak, hata # 1 yine de tekrarlanabilir.
Bu hataya yönelik orijinal düzeltmemiz, IIS çekirdeği / kullanıcı modu önbelleğini yalnızca diğerleri gibi Kaset istekleri için devre dışı bırakmaktı. Bunu yaparak, web sunucularımızın önünde fazladan bir önbellek katmanı dağıtırken 1 numaralı hatayı ortaya çıkardık. Çünkü sorgu dize kesmek çalıştı nedeni OutputCacheModule
ise bir önbellek kaydeder Cache
API bağlı olarak değişebileceği için kullanılmamış QueryString
ve istek bir sahipseQueryString
.
Geçici çözüm
Yine de Cassette'den uzaklaşmayı planlıyoruz, bu nedenle kendi kaset çatalımızı korumak (veya bir PR birleştirmeye çalışmak) yerine, bu soruna geçici bir çözüm bulmak için bir HTTP modülü kullanmayı tercih ettik.
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()
{
}
}
Umarım bu birine yardımcı olur 😄!