Kami juga mengalami bug ini tetapi kami menggunakan perpustakaan manajemen aset (Kaset). Setelah penyelidikan yang ekstensif tentang masalah ini, kami telah menemukan bahwa akar penyebab masalah ini adalah dengan kombinasi ASP.NET, IIS, dan Cassette. Saya tidak yakin apakah ini masalah Anda (menggunakan Headers
API dan bukan Cache
API), tetapi polanya tampaknya sama.
Bug # 1
Kaset menetapkan Vary: Accept-Encoding
header sebagai bagian dari responsnya terhadap sebuah bundel karena ia dapat menyandikan konten dengan gzip / deflate:
Namun, cache output ASP.NET akan selalu mengembalikan respons yang di-cache terlebih dahulu. Misalnya, jika permintaan pertama memiliki Accept-Encoding: gzip
dan Cassette mengembalikan konten yang di-gzip, cache output ASP.NET akan men-cache URL sebagai Content-Encoding: gzip
. Permintaan selanjutnya ke URL yang sama tetapi dengan pengkodean yang dapat diterima yang berbeda (mis. Accept-Encoding: deflate
) Akan mengembalikan respons yang di-cache Content-Encoding: gzip
.
Bug ini disebabkan oleh Cassette menggunakan HttpResponseBase.Cache
API untuk mengatur pengaturan cache output (misalnya Cache-Control: public
) tetapi menggunakan HttpResponseBase.Headers
API untuk mengatur Vary: Accept-Encoding
header. Masalahnya adalah bahwa ASP.NET OutputCacheModule
adalah tidak menyadari header respon; itu hanya berfungsi melalui Cache
API. Artinya, mereka mengharapkan pengembang untuk menggunakan API yang digabungkan secara ketat dan tidak terlihat, dan bukan hanya HTTP standar.
Bug # 2
Saat menggunakan IIS 7.5 (Windows Server 2008 R2), bug # 1 dapat menyebabkan masalah terpisah dengan IIS kernel dan cache pengguna. Misalnya, setelah bundel berhasil di-cache dengan Content-Encoding: gzip
, dimungkinkan untuk melihatnya di cache kernel IIS dengan netsh http show cachestate
. Ini menunjukkan respons dengan 200 kode status dan pengkodean konten "gzip". Jika permintaan berikutnya memiliki pengkodean yang berbeda dapat diterima (misalnya
Accept-Encoding: deflate
) dan sebuah If-None-Match
header yang cocok hash bundel ini, permintaan ke kernel dan modus pengguna cache IIS akan dianggap sebagai kehilangan . Dengan demikian, menyebabkan permintaan ditangani oleh Cassette yang mengembalikan 304:
Namun, setelah mode kernel dan pengguna IIS memproses respons, mereka akan melihat bahwa respons untuk URL telah berubah dan cache harus diperbarui. Jika cache kernel IIS dicek netsh http show cachestate
lagi, respons cache 200 diganti dengan respons 304. Semua permintaan berikutnya pada bundel, terlepas dari Accept-Encoding
dan If-None-Match
akan mengembalikan respons 304. Kami melihat efek buruk dari bug ini di mana semua pengguna mendapatkan 304 untuk skrip inti kami karena permintaan acak yang tidak terduga Accept-Encoding
dan If-None-Match
.
Masalahnya tampaknya bahwa IIS kernel dan cache mode pengguna tidak dapat bervariasi berdasarkan Accept-Encoding
header. Sebagai bukti dari ini, dengan menggunakan Cache
API dengan solusi di bawah ini, IIS kernel dan mode cache pengguna tampaknya selalu dilewati (hanya cache output ASP.NET digunakan). Ini dapat dikonfirmasi dengan memeriksa yang netsh http show cachestate
kosong dengan solusi di bawah ini. ASP.NET berkomunikasi dengan pekerja IIS secara langsung untuk mengaktifkan atau menonaktifkan kernel IIS dan mode cache pengguna per permintaan.
Kami tidak dapat mereproduksi bug ini pada versi IIS yang lebih baru (mis. IIS Express 10). Namun, bug # 1 masih dapat direproduksi.
Perbaikan asli kami untuk bug ini adalah untuk menonaktifkan caching mode kernel / pengguna IIS hanya untuk permintaan Cassette seperti yang disebutkan lainnya. Dengan melakukannya, kami menemukan bug # 1 ketika menggunakan lapisan caching tambahan di depan server web kami. Alasan peretasan string kueri berfungsi adalah karena OutputCacheModule
akan mencatat cache yang hilang jika Cache
API belum digunakan untuk bervariasi berdasarkan pada QueryString
dan jika permintaan memilikiQueryString
.
Penanganan masalah
Kami telah berencana untuk pindah dari Cassette, jadi daripada mempertahankan garpu Cassette kami sendiri (atau mencoba untuk mendapatkan penggabungan PR), kami memilih untuk menggunakan modul HTTP untuk mengatasi masalah ini.
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()
{
}
}
Saya harap ini membantu seseorang 😄!