เราประสบปัญหานี้ด้วยเช่นกัน แต่เรากำลังใช้ไลบรารีการจัดการสินทรัพย์ (Cassette) หลังจากการตรวจสอบอย่างละเอียดของปัญหานี้เราพบว่าสาเหตุของปัญหานี้คือการรวมกันของ ASP.NET, IIS และ Cassette ฉันไม่แน่ใจว่านี่เป็นปัญหาของคุณหรือไม่ (ใช้Headers
API แทนCache
API) แต่รูปแบบดูเหมือนจะเหมือนกัน
ข้อผิดพลาด # 1
Cassette ตั้งค่าVary: Accept-Encoding
ส่วนหัวเป็นส่วนหนึ่งของการตอบสนองต่อบันเดิลเนื่องจากสามารถเข้ารหัสเนื้อหาด้วย gzip / deflate:
อย่างไรก็ตามแคชเอาต์พุต ASP.NET จะส่งคืนการตอบกลับที่แคชไว้เสมอ ตัวอย่างเช่นหากคำขอแรกมีAccept-Encoding: gzip
และ Cassette ส่งคืนเนื้อหา gzipped แคชเอาต์พุต ASP.NET จะแคช URL Content-Encoding: gzip
ดังนี้ การร้องขอต่อไปยัง URL เดียวกัน แต่มีการเข้ารหัสที่ยอมรับที่แตกต่างกัน (เช่นAccept-Encoding: deflate
) Content-Encoding: gzip
จะกลับการตอบสนองที่เก็บไว้ชั่วคราวด้วย
ข้อผิดพลาดนี้เกิดจาก Cassette โดยใช้HttpResponseBase.Cache
API เพื่อตั้งค่าแคชเอาต์พุต (เช่นCache-Control: public
) แต่ใช้HttpResponseBase.Headers
API เพื่อตั้งค่าVary: Accept-Encoding
ส่วนหัว ปัญหาคือว่า ASP.NET OutputCacheModule
คือไม่ได้ตระหนักถึงส่วนหัวของการตอบสนอง ใช้งานได้ผ่านCache
API เท่านั้น นั่นคือคาดว่านักพัฒนาซอฟต์แวร์จะใช้ API คู่ที่แน่นหนามากกว่า HTTP มาตรฐานเพียงอย่างเดียว
ข้อผิดพลาด # 2
เมื่อใช้ IIS 7.5 (Windows Server 2008 R2) ข้อผิดพลาด # 1 สามารถทำให้เกิดปัญหาแยกต่างหากกับเคอร์เนล IIS และแคชผู้ใช้ ตัวอย่างเช่นเมื่อมัดแคชประสบความสำเร็จกับContent-Encoding: gzip
ความเป็นไปได้ที่จะเห็นมันใน IIS netsh http show cachestate
เคอร์เนลแคชกับ มันแสดงการตอบสนองด้วยรหัสสถานะ 200 และการเข้ารหัสเนื้อหาของ "gzip" หากคำขอต่อไปมีการเข้ารหัสที่แตกต่างกันที่ยอมรับได้ (เช่น
Accept-Encoding: deflate
) และIf-None-Match
ส่วนหัวที่ตรงกับกัญชาห่อของคำขอเข้า IIS ของเคอร์เนลและโหมดผู้ใช้แคชจะได้รับการพิจารณาพลาด ดังนั้นทำให้การร้องขอถูกจัดการโดย Cassette ซึ่งส่งคืน 304:
อย่างไรก็ตามเมื่อเคอร์เนลและโหมดผู้ใช้ของ IIS ประมวลผลการตอบสนองพวกเขาจะเห็นว่าการตอบสนองสำหรับ URL นั้นมีการเปลี่ยนแปลงและควรปรับปรุงแคช ถ้าแคชเคอร์เนล IIS ถูกตรวจสอบnetsh http show cachestate
อีกครั้งการตอบสนอง 200 แคชจะถูกแทนที่ด้วยการตอบสนอง 304 คำร้องขอที่ตามมาทั้งหมดไปยังบันเดิลโดยไม่คำนึงถึงAccept-Encoding
และIf-None-Match
จะส่งคืนการตอบกลับ 304 เราเห็นผลกระทบของปัญหานี้ที่ผู้ใช้ทุกคนถูกนำมาเสิร์ฟ 304 สำหรับสคริปต์หลักของเราเพราะการร้องขอการสุ่มที่มีที่ไม่คาดคิดและAccept-Encoding
If-None-Match
ปัญหาน่าจะเป็นที่ IIS kernel และแคชโหมดผู้ใช้จะไม่สามารถแตกต่างกันไปขึ้นอยู่กับAccept-Encoding
ส่วนหัว จากหลักฐานนี้โดยใช้Cache
API กับวิธีแก้ปัญหาด้านล่างเคอร์เนล IIS และแคชโหมดผู้ใช้จะถูกข้ามไปเสมอ (เฉพาะแคชเอาต์พุต ASP.NET เท่านั้น) สิ่งนี้สามารถยืนยันได้โดยการตรวจสอบว่าnetsh http show cachestate
ว่างเปล่าด้วยวิธีแก้ไขปัญหาด้านล่าง ASP.NET สื่อสารกับผู้ปฏิบัติงาน IIS โดยตรงเพื่อเลือกเปิดหรือปิดใช้งานเคอร์เนล IIS และโหมดผู้ใช้แคชต่อคำขอ
เราไม่สามารถสร้างข้อผิดพลาดนี้ใน IIS เวอร์ชันที่ใหม่กว่า (เช่น IIS Express 10) อย่างไรก็ตามข้อผิดพลาด # 1 ยังคงทำซ้ำได้
การแก้ไขดั้งเดิมของเราสำหรับข้อผิดพลาดนี้คือการปิดใช้งานการแคช IIS kernel / โหมดผู้ใช้เฉพาะสำหรับคำขอ Cassette เช่นที่คนอื่น ๆ กล่าวถึง ด้วยการทำเช่นนี้เราได้เปิดข้อบกพร่อง # 1 เมื่อทำการปรับใช้เลเยอร์เพิ่มเติมของการแคชหน้าเว็บเซิร์ฟเวอร์ของเรา ด้วยเหตุผลที่ว่าสตริงแบบสอบถามทำงานสับเป็นเพราะOutputCacheModule
จะบันทึกแคชพลาดถ้าCache
API ยังไม่ได้ถูกนำมาใช้จะแตกต่างกันขึ้นอยู่กับQueryString
และQueryString
ถ้าขอมี
วิธีแก้ปัญหา
เราได้วางแผนที่จะย้ายออกจาก Cassette ต่อไปดังนั้นแทนที่จะรักษา Cassette ของเราเอง (หรือพยายามที่จะรวมการประชาสัมพันธ์) เราเลือกใช้โมดูล HTTP เพื่อแก้ไขปัญหานี้
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()
{
}
}
ฉันหวังว่านี่จะช่วยให้ใครบางคน😄!