ป้องกันการแคชใน ASP.NET MVC สำหรับการกระทำที่เจาะจงโดยใช้แอตทริบิวต์


197

ฉันมีแอปพลิเคชัน ASP.NET MVC 3 แอปพลิเคชันนี้ขอบันทึกผ่าน jQuery jQuery โทรกลับไปยังแอ็คชันคอนโทรลเลอร์ที่ส่งคืนผลลัพธ์ในรูปแบบ JSON ฉันไม่สามารถพิสูจน์ได้ แต่ฉันกังวลว่าข้อมูลของฉันอาจถูกแคช

ฉันต้องการให้แคชใช้กับการกระทำบางอย่างเท่านั้นไม่ใช่สำหรับการกระทำทั้งหมด

มีแอตทริบิวต์ที่ฉันสามารถดำเนินการเพื่อให้แน่ใจว่าข้อมูลไม่ได้ถูกแคชหรือไม่ หากไม่ฉันจะมั่นใจได้อย่างไรว่าเบราว์เซอร์ได้รับชุดระเบียนใหม่ทุกครั้งแทนที่จะเป็นชุดแคช


1
หากคุณคาดเดาว่ามีบางสิ่งที่แคชอยู่ฉันขอแนะนำให้คุณอ่านกลไกการควบคุมแคชที่นี่: w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.9

คำตอบ:


304

เพื่อให้แน่ใจว่า JQuery ไม่ได้แคชผลลัพธ์ในวิธี ajax ของคุณให้ใส่สิ่งต่อไปนี้:

$.ajax({
    cache: false
    //rest of your ajax setup
});

หรือเพื่อป้องกันการแคชใน MVC เราได้สร้างคุณลักษณะของเราเองคุณสามารถทำเช่นเดียวกัน นี่คือรหัสของเรา:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : ActionFilterAttribute
{
    public override void OnResultExecuting(ResultExecutingContext filterContext)
    {
        filterContext.HttpContext.Response.Cache.SetExpires(DateTime.UtcNow.AddDays(-1));
        filterContext.HttpContext.Response.Cache.SetValidUntilExpires(false);
        filterContext.HttpContext.Response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
        filterContext.HttpContext.Response.Cache.SetCacheability(HttpCacheability.NoCache);
        filterContext.HttpContext.Response.Cache.SetNoStore();

        base.OnResultExecuting(filterContext);
    }
}

[NoCache]แล้วก็ตกแต่งควบคุมของคุณด้วย หรือทำทุกสิ่งที่คุณทำได้เพียงใส่แอททริบิวในคลาสของคลาสพื้นฐานที่คุณสืบทอดคอนโทรลเลอร์ของคุณจาก (ถ้ามี) ที่เรามีอยู่ที่นี่:

[NoCache]
public class ControllerBase : Controller, IControllerBase

คุณยังสามารถตกแต่งแอ็คชั่นบางอย่างด้วยแอททริบิวต์นี้หากคุณต้องการให้มันไม่สามารถแคชได้แทนที่จะตกแต่งทั้งคอนโทรลเลอร์

หากคลาสหรือการกระทำของคุณไม่มีNoCacheเมื่อแสดงผลในเบราว์เซอร์ของคุณและคุณต้องการตรวจสอบว่ามันใช้งานได้โปรดจำไว้ว่าหลังจากรวบรวมการเปลี่ยนแปลงคุณต้องทำ "รีเฟรชอย่างหนัก" (Ctrl + F5) ในเบราว์เซอร์ของคุณ เบราว์เซอร์ของคุณจะเก็บเวอร์ชันเก่าไว้และจะไม่รีเฟรชด้วย "รีเฟรชปกติ" (F5)


1
ฉันลองทุกอย่างในวิธีแก้ปัญหาด้านบนและไม่ได้ผลสำหรับฉัน
โอบีวาน

9
มันเป็นความเข้าใจของฉัน (และฉันไม่ใช่ผู้เชี่ยวชาญ jQuery) แคชนั้น: เท็จทำให้ jQuery ติดอยู่กับสตริงการสืบค้นเปลี่ยนค่าเป็น "หลอก" เบราว์เซอร์ในการคิดคำขอเป็นอย่างอื่น ในทางทฤษฎีนี่หมายความว่าเบราว์เซอร์จะยังคงแคชผลลัพธ์อยู่ แต่จะไม่ใช้ผลลัพธ์ที่แคช ควรมีประสิทธิภาพมากขึ้นสำหรับไคลเอ็นต์เพื่อปิดใช้งานการแคชผ่านส่วนหัวการตอบกลับ
Josh

2
ทำงานบนระดับคอนโทรลเลอร์เท่านั้นและไม่ทำงานในระดับการทำงาน
Ramesh

3
ฉันจะ upvote รวมถึงคุณลักษณะดังกล่าวในแพ็คเกจ ASP.NET อย่างเป็นทางการ :-)
usr-local-ΕΨΗΕΛΩΝ

1
@ Frédéricส่วนของข้อมูลจำเพาะที่คุณระบุว่าแคชไม่สามารถแคชเนื้อหาที่ไม่มีการจัดเก็บ: คำสั่งการตอบสนอง "ไม่มีการจัดเก็บ" บ่งชี้ว่าแคชไม่ต้องจัดเก็บส่วนใดส่วนหนึ่งของคำขอหรือการตอบสนองทันที
kristianp

258

คุณสามารถใช้แอตทริบิวต์แคชในตัวเพื่อป้องกันการแคช

สำหรับ. net Framework: [OutputCache(NoStore = true, Duration = 0)]

สำหรับ. net Core: [ResponseCache(NoStore = true, Duration = 0)]

โปรดระวังว่าเป็นไปไม่ได้ที่จะบังคับให้เบราว์เซอร์ปิดใช้งานการแคช สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือให้คำแนะนำที่เบราว์เซอร์ส่วนใหญ่จะให้เกียรติโดยปกติจะอยู่ในรูปแบบของส่วนหัวหรือเมตาแท็ก Cache-Control: public, no-store, max-age=0แอตทริบิวต์มัณฑนานี้จะปิดการใช้งานเซิร์ฟเวอร์แคชและยังเพิ่มส่วนหัวนี้: มันไม่ได้เพิ่มเมตาแท็ก หากต้องการสามารถเพิ่มได้ด้วยตนเองในมุมมอง

นอกจากนี้ JQuery และเฟรมเวิร์กไคลเอนต์อื่น ๆ จะพยายามหลอกเบราว์เซอร์ให้ไม่ใช้ทรัพยากรที่เก็บในเวอร์ชันแคชโดยการเพิ่มเนื้อหาลงใน url เช่น timestamp หรือ GUID วิธีนี้มีประสิทธิภาพในการทำให้เบราว์เซอร์ร้องขอทรัพยากรอีกครั้ง แต่ไม่ได้ป้องกันการแคชจริงๆ

เมื่อทราบขั้นสุดท้าย คุณควรระวังว่าทรัพยากรสามารถถูกแคชระหว่างเซิร์ฟเวอร์และไคลเอนต์ ISP พร็อกซีและอุปกรณ์เครือข่ายอื่น ๆ ของ ISP ยังแคชทรัพยากรและพวกเขามักจะใช้กฎภายในโดยไม่ต้องดูทรัพยากรจริง มีไม่มากที่คุณสามารถทำเกี่ยวกับสิ่งเหล่านี้ ข่าวดีก็คือว่าพวกเขามักจะแคชสำหรับกรอบเวลาที่สั้นลงเช่นวินาทีหรือนาที


3
ฉันเชื่อว่าสิ่งนี้ไม่ได้ตอบคำถามอย่างเต็มที่ สิ่งนี้ปิดใช้งานการแคช ASP.NET แต่ไม่ใช่การแคชเบราว์เซอร์
Rosdi Kasim

22
ไม่สามารถบังคับให้เบราว์เซอร์ปิดใช้งานการแคชได้ สิ่งที่ดีที่สุดที่คุณสามารถทำได้คือให้คำแนะนำที่เบราว์เซอร์ส่วนใหญ่จะให้เกียรติโดยปกติจะอยู่ในรูปแบบของส่วนหัวหรือเมตาแท็ก แอตทริบิวต์มัณฑนานี้จะปิดการใช้งานเซิร์ฟเวอร์แคช .NET Cache-Control:public, no-store, max-age=0และยังเพิ่มส่วนหัว มันไม่ได้เพิ่มเมตาแท็ก หากต้องการสามารถเพิ่มได้ด้วยตนเองในมุมมอง
Jaguir

1
ฉันสามารถเข้าใจได้ว่าทำไมคุณถึงใช้NoStore = trueและDuration = 0(ซึ่งฉันใช้สำเร็จแล้วขอบคุณ) แต่เอฟเฟกต์เพิ่มเติมอะไรที่จะVaryByParam = "None"มีเนื่องจากตัวเลือกสองตัวเลือกอื่น ๆ ที่มีผลต่อคำขอทั้งหมดโดยไม่คำนึงถึงพารามิเตอร์
Gone Coding

ฉันไม่คิดว่ามันจำเป็นต้องใช้ใน MVC ฉันแค่ชัดเจน ฉันจำได้ว่าในเว็บฟอร์ม ASP.NET และการควบคุมผู้ใช้จำเป็นต้องมีแอตทริบิวต์นี้หรือแอตทริบิวต์ VaryByControl
Jaguir

1
สำหรับ ASP.NET Core ให้ใช้: '[ResponseCache (NoStore = true, Duration = 0)]'
Jeff

48

สิ่งที่คุณต้องการคือ:

[OutputCache(Duration=0)]
public JsonResult MyAction(

หรือถ้าคุณต้องการปิดการใช้งานมันสำหรับคอนโทรลเลอร์ทั้งหมด:

[OutputCache(Duration=0)]
public class MyController

แม้จะมีการอภิปรายในความคิดเห็นที่นี่ก็เพียงพอที่จะปิดการใช้งานแคชเบราว์เซอร์ - ซึ่งทำให้ ASP.Net ปล่อยส่วนหัวการตอบสนองที่บอกเบราว์เซอร์เอกสารหมดอายุในทันที:

OutputCache Duration = 0 ส่วนหัวการตอบกลับ: max-age = 0, s-maxage = 0


6
IE8 ยังคงแสดงผลเวอร์ชันที่แคชของหน้าเมื่อคลิกปุ่มย้อนกลับโดยใช้เฉพาะ Duration = 0 ใน Controller Action ใช้ NoStore = true พร้อมกับ Duration = 0 (ดูคำตอบของ Jared) แก้ไขพฤติกรรมในกรณีของฉัน
Keith Ketterer

3
สิ่งนี้มีพฤติกรรมที่อยากรู้อยากเห็นในการตั้งค่าCache-Controlเป็นpublic
ta.speot.is

max-age=0ไม่เคยหมายถึง 'แคชถูกปิดใช้งาน' สิ่งนี้หมายความว่าเนื้อหาการตอบสนองจะได้รับการพิจารณาว่าค้างทันทีแต่อนุญาตให้แคชแคชได้ เบราว์เซอร์ควรตรวจสอบความสดใหม่ของเนื้อหาเก่าที่แคชไว้ก่อนใช้ แต่ไม่บังคับเว้นแต่must-revalidateจะระบุคำสั่งเพิ่มเติม
Frédéric

14

ในการดำเนินการควบคุมผนวกเข้ากับส่วนหัวบรรทัดต่อไปนี้

    public ActionResult Create(string PositionID)
    {
        Response.AppendHeader("Cache-Control", "no-cache, no-store, must-revalidate"); // HTTP 1.1.
        Response.AppendHeader("Pragma", "no-cache"); // HTTP 1.0.
        Response.AppendHeader("Expires", "0"); // Proxies.

5

นี่คือNoCacheคุณสมบัติที่เสนอโดย mattytommo ซึ่งทำให้ง่ายขึ้นโดยใช้ข้อมูลจากคำตอบของ Chris Moschini:

[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
public sealed class NoCacheAttribute : OutputCacheAttribute
{
    public NoCacheAttribute()
    {
        this.Duration = 0;
    }
}

ด้วยเหตุผลบางอย่าง MVC 3 ไม่เพียงให้คุณกำหนดระยะเวลาเป็น 0 คุณต้องเพิ่มคำอธิบายประกอบเหล่านี้ ... ขอบคุณสำหรับการแก้ปัญหา!
micahhoover

max-age=0ไม่เคยหมายถึง 'แคชถูกปิดใช้งาน' สิ่งนี้หมายความว่าเนื้อหาการตอบสนองจะได้รับการพิจารณาว่าค้างทันทีแต่อนุญาตให้แคชแคชได้ เบราว์เซอร์ควรตรวจสอบความสดใหม่ของเนื้อหาเก่าที่แคชไว้ก่อนใช้ แต่ไม่บังคับเว้นแต่must-revalidateจะระบุคำสั่งเพิ่มเติม
Frédéric

เพื่อความสมบูรณ์คำสั่งขั้นต่ำและเหมาะสมที่สุดคือno-cacheซึ่งยังคงอนุญาตให้แคช แต่ได้รับคำสั่งให้ตรวจสอบความถูกต้องบนเซิร์ฟเวอร์ต้นทางก่อนการใช้งานใด ๆ เพื่อหลีกเลี่ยงการตรวจสอบอีกครั้งแคชแม้คุณจะมีการเพิ่มพร้อมกับno-store no-cache( no-storeคนเดียวเป็นความผิดที่ชัดถ้อยชัดคำเพราะแคชระเหยได้รับอนุญาตให้เนื้อหาแคชทำเครื่องหมายเป็นno-store.)
Frédéric

4

สำหรับ MVC6 ( DNX ) ไม่มีSystem.Web.OutputCacheAttribute

หมายเหตุ: เมื่อคุณตั้งค่าNoStoreพารามิเตอร์ Duration จะไม่ถูกนำมาพิจารณา เป็นไปได้ที่จะกำหนดระยะเวลาเริ่มต้นสำหรับการลงทะเบียนครั้งแรกและแทนที่สิ่งนี้ด้วยคุณสมบัติที่กำหนดเอง

แต่เรามี Microsoft.AspNet.Mvc.Filters.ResponseCacheFilter

 public void ConfigureServices(IServiceCollection services)
        ...
        services.AddMvc(config=>
        {
            config.Filters.Add(
                 new ResponseCacheFilter(
                    new CacheProfile() { 
                      NoStore=true
                     }));
        }
        ...
       )

เป็นไปได้ที่จะแทนที่ตัวกรองเริ่มต้นด้วยแอตทริบิวต์ที่กำหนดเอง

    [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
    public sealed class NoCacheAttribute : ActionFilterAttribute
    {
        public override void OnResultExecuting(ResultExecutingContext filterContext)
        {
            var filter=filterContext.Filters.Where(t => t.GetType() == typeof(ResponseCacheFilter)).FirstOrDefault();
            if (filter != null)
            {
                ResponseCacheFilter f = (ResponseCacheFilter)filter;
                f.NoStore = true;
                //f.Duration = 0;
            }

            base.OnResultExecuting(filterContext);
        }
    }

นี่คือกรณีการใช้งาน

    [NoCache]
    [HttpGet]
    public JsonResult Get()
    {            
        return Json(new DateTime());
    }

1

เอาท์พุทแคชใน MVC

[OutputCache (NoStore = true, Duration = 0, Location = "None", VaryByParam = "*")]

หรือ
[OutputCache (NoStore = true, Duration = 0, VaryByParam = "ไม่มี")]


ดูความคิดเห็นอื่น ๆ ( 1 , 2 , 3 ) กับคำตอบมากมายที่แนะนำการใช้สิ่งนี้แล้ว บรรทัดที่สองของคุณผิดและจะนำไปสู่ปัญหากับเบราว์เซอร์บางตัว
Frédéric

1

ค่าแอตทริบิวต์ที่ถูกต้องสำหรับAsp.Net MVC Coreเพื่อป้องกันการแคชของเบราว์เซอร์ (รวมถึงInternet Explorer 11 ) คือ:

[ResponseCache(Location = ResponseCacheLocation.None, NoStore = true)]

ตามที่อธิบายไว้ในเอกสารของ Microsoft:

การตอบสนองแคชใน ASP.NET Core - NoStore และ Location.None


0

โซลูชัน ASP.NET MVC 5:

  1. แคชรหัสป้องกันที่ตั้งอยู่ใจกลางเมืองที่: App_Start/FilterConfig.cs's RegisterGlobalFiltersวิธีการ:
    public class FilterConfig
    {
        public static void RegisterGlobalFilters(GlobalFilterCollection filters)
        {
            // ...
            filters.Add(new OutputCacheAttribute
            {
                NoStore = true,
                Duration = 0,
                VaryByParam = "*",
                Location = System.Web.UI.OutputCacheLocation.None
            });
        }
    }
  1. เมื่อคุณมีสิ่งที่คุณเข้าใจฉันก็คือคุณสามารถแทนที่ตัวกรองทั่วโลกโดยใช้OutputCacheคำสั่งที่แตกต่างกันในระดับControllerหรือ Viewสำหรับผู้ควบคุมปกติมัน
[OutputCache(NoStore = true, Duration = 0, Location=System.Web.UI.ResponseCacheLocation.None, VaryByParam = "*")]

หรือถ้ามันเป็นApiControllerมันจะเป็น

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, Location = System.Web.UI.OutputCacheLocation.None, VaryByParam = "*")]
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.