Ngăn chặn bộ đệm trong ASP.NET MVC cho các hành động cụ thể bằng cách sử dụng một thuộc tính


196

Tôi có một ứng dụng ASP.NET MVC 3. Ứng dụng này yêu cầu các bản ghi thông qua jQuery. jQuery gọi lại một hành động của bộ điều khiển trả về kết quả ở định dạng JSON. Tôi chưa thể chứng minh điều này, nhưng tôi lo ngại rằng dữ liệu của tôi có thể bị lưu vào bộ nhớ cache.

Tôi chỉ muốn bộ nhớ đệm được áp dụng cho các hành động cụ thể, không phải cho tất cả các hành động.

Có một thuộc tính mà tôi có thể đưa ra một hành động để đảm bảo rằng dữ liệu không được lưu vào bộ nhớ cache không? Nếu không, làm thế nào để tôi đảm bảo rằng trình duyệt nhận được một bộ bản ghi mới mỗi lần, thay vì một bộ được lưu trữ?


1
Nếu bạn đoán rằng một cái gì đó đang được lưu trong bộ nhớ cache, thì tôi khuyên bạn nên đọc các cơ chế kiểm soát bộ đệm ở đây: w3.org/Prot Protocol / rfc2616 / rfc2616

Câu trả lời:


304

Để đảm bảo rằng JQuery không lưu trữ kết quả, trên các phương thức ajax của bạn, hãy đặt như sau:

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

Hoặc để ngăn chặn bộ đệm trong MVC, chúng tôi đã tạo thuộc tính của riêng mình, bạn có thể làm tương tự. Đây là mã của chúng tôi:

[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);
    }
}

Sau đó, chỉ cần trang trí bộ điều khiển của bạn với [NoCache]. HOẶC để làm điều đó cho tất cả những gì bạn có thể chỉ cần đặt thuộc tính vào lớp của lớp cơ sở mà bạn kế thừa bộ điều khiển của mình từ (nếu bạn có) như chúng tôi có ở đây:

[NoCache]
public class ControllerBase : Controller, IControllerBase

Bạn cũng có thể trang trí một số hành động với thuộc tính này nếu bạn cần chúng không được lưu trong bộ nhớ cache, thay vì trang trí toàn bộ bộ điều khiển.

Nếu lớp hoặc hành động của bạn không có NoCachekhi nó được hiển thị trong trình duyệt của bạn và bạn muốn kiểm tra xem nó có hoạt động không, hãy nhớ rằng sau khi biên dịch các thay đổi bạn cần thực hiện "làm mới cứng" (Ctrl + F5) trong trình duyệt của bạn. Cho đến khi bạn làm như vậy, trình duyệt của bạn sẽ giữ phiên bản được lưu trong bộ nhớ cache cũ và sẽ không làm mới nó bằng "làm mới bình thường" (F5).


1
Tôi đã thử mọi cách trong giải pháp trên và nó không hiệu quả với tôi.
Obi Wan

9
Theo hiểu biết của tôi (và tôi không phải là chuyên gia về jQuery) rằng bộ đệm: false chỉ khiến jQuery xử lý chuỗi truy vấn thành một giá trị thay đổi để "lừa" trình duyệt nghĩ rằng yêu cầu là dành cho thứ khác. Về lý thuyết, điều này có nghĩa là trình duyệt vẫn lưu trữ kết quả, nhưng sẽ không sử dụng kết quả được lưu trong bộ nhớ cache. Nên hiệu quả hơn trên máy khách để vô hiệu hóa bộ đệm ẩn thông qua các tiêu đề phản hồi.
Josh

2
Chỉ hoạt động ở cấp độ bộ điều khiển và không hoạt động ở cấp độ hành động.
Ramesh

3
Tôi sẽ nâng cấp bao gồm một thuộc tính như vậy trong gói ASP.NET chính thức :-)
usr-local-ΕΨΗΕΛΩΝ

1
@ Frédéric, phần thông số kỹ thuật mà bạn chỉ ra nói rằng bộ nhớ cache không thể lưu trữ nội dung không lưu trữ: Chỉ thị phản hồi "không lưu trữ" chỉ ra rằng bộ đệm KHÔNG PHẢI lưu trữ bất kỳ phần nào của yêu cầu hoặc phản hồi ngay lập tức.
kristianp

258

Bạn có thể sử dụng thuộc tính bộ nhớ cache tích hợp để ngăn chặn bộ đệm.

Đối với .net Framework: [OutputCache(NoStore = true, Duration = 0)]

Đối với lõi .net: [ResponseCache(NoStore = true, Duration = 0)]

Xin lưu ý rằng không thể buộc trình duyệt tắt bộ nhớ đệm. Điều tốt nhất bạn có thể làm là cung cấp các đề xuất mà hầu hết các trình duyệt sẽ tôn vinh, thường là ở dạng tiêu đề hoặc thẻ meta. Thuộc tính trang trí này sẽ vô hiệu hóa bộ nhớ đệm máy chủ và cũng thêm tiêu đề này : Cache-Control: public, no-store, max-age=0. Nó không thêm thẻ meta. Nếu muốn, những cái đó có thể được thêm bằng tay trong chế độ xem.

Ngoài ra, JQuery và các khung máy khách khác sẽ cố gắng lừa trình duyệt không sử dụng phiên bản tài nguyên được lưu trong bộ nhớ cache của nó bằng cách thêm nội dung vào url, như dấu thời gian hoặc GUID. Điều này có hiệu quả trong việc khiến trình duyệt yêu cầu tài nguyên một lần nữa nhưng không thực sự ngăn chặn bộ đệm.

Trên một lưu ý cuối cùng. Bạn nên lưu ý rằng các tài nguyên cũng có thể được lưu trong bộ đệm giữa máy chủ và máy khách. ISP, proxy và các thiết bị mạng khác cũng lưu trữ tài nguyên và họ thường sử dụng các quy tắc nội bộ mà không cần nhìn vào tài nguyên thực tế. Bạn không thể làm gì nhiều về những điều này. Tin tốt là họ thường lưu trữ bộ đệm trong các khung thời gian ngắn hơn, như giây hoặc phút.


3
Tôi tin rằng điều này không hoàn toàn giải quyết câu hỏi. Điều này vô hiệu hóa bộ đệm ASP.NET nhưng không lưu bộ nhớ cache của trình duyệt.
Rosdi Kasim

22
Không thể buộc trình duyệt tắt bộ nhớ đệm. Điều tốt nhất bạn có thể làm là cung cấp các đề xuất mà hầu hết các trình duyệt sẽ tôn vinh, thường là ở dạng tiêu đề hoặc thẻ meta. Thuộc tính trang trí này sẽ vô hiệu hóa bộ đệm máy chủ .NET và cũng thêm tiêu đề Cache-Control:public, no-store, max-age=0. Nó không thêm thẻ meta. Nếu muốn, những cái đó có thể được thêm bằng tay trong chế độ xem.
Jaguir

1
Tôi có thể hiểu lý do tại sao bạn sẽ sử dụng NoStore = trueDuration = 0(mà tôi đã sử dụng thành công, cảm ơn), nhưng hiệu ứng bổ sung nào sẽ VaryByParam = "None"có vì hai tùy chọn khác ảnh hưởng đến tất cả các yêu cầu bất kể tham số là gì?
Mã hóa

Tôi không nghĩ rằng nó được yêu cầu trong MVC, tôi chỉ rõ ràng. Tôi nhớ rằng trong các biểu mẫu web ASP.NET và điều khiển người dùng, thuộc tính này hoặc thuộc tính VaryByControl là bắt buộc.
Jaguir

1
Để sử dụng ASP.NET Core: '[FeedbackCache (NoStore = true, Thời gian = 0)]'
Jeff

48

Tất cả bạn cần là:

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

hoặc, nếu bạn muốn tắt nó cho toàn bộ Bộ điều khiển:

[OutputCache(Duration=0)]
public class MyController

Bất chấp các cuộc tranh luận trong các bình luận ở đây, điều này là đủ để vô hiệu hóa bộ nhớ đệm của trình duyệt - điều này khiến cho ASP.Net phát ra các tiêu đề phản hồi cho trình duyệt biết tài liệu hết hạn ngay lập tức:

Thời lượng đầu ra = 0 Tiêu đề phản hồi: max-age = 0, s-maxage = 0


6
IE8 vẫn hiển thị phiên bản được lưu trong bộ nhớ cache của trang khi nhấp vào nút quay lại chỉ sử dụng Thời lượng = 0 trên Bộ điều khiển hành động. Sử dụng NoStore = true cùng với Thời gian = 0 (xem câu trả lời của Jared) đã sửa lỗi hành vi trong trường hợp của tôi.
Keith Ketterer

3
Điều này có hành vi hơi tò mò khi đặt Cache-Controlthànhpublic
ta.speot.is

max-age=0chưa bao giờ có nghĩa là 'bộ nhớ cache bị vô hiệu hóa'. Điều này không có nghĩa là nội dung phản hồi sẽ được xem là ngay lập tức , nhưng bộ đệm được phép lưu vào bộ đệm. Các trình duyệt nên xác nhận tính mới của nội dung cũ được lưu trong bộ nhớ cache trước khi sử dụng, nhưng nó không bắt buộc trừ khi chỉ thị bổ sung must-revalidateđược chỉ định.
Frédéric

14

Trong hành động điều khiển nối vào tiêu đề các dòng sau

    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

Đây là NoCachethuộc tính được đề xuất bởi mattytommo, được đơn giản hóa bằng cách sử dụng thông tin từ câu trả lời của Chris Moschini:

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

Vì một số lý do, MVC 3 không chỉ cho phép bạn đặt thời lượng thành 0. Bạn phải thêm các chú thích này ... cảm ơn vì cách giải quyết!
micahhoover 19/03/2015

max-age=0chưa bao giờ có nghĩa là 'bộ nhớ cache bị vô hiệu hóa'. Điều này không có nghĩa là nội dung phản hồi sẽ được xem là ngay lập tức , nhưng bộ đệm được phép lưu vào bộ đệm. Các trình duyệt nên xác nhận tính mới của nội dung cũ được lưu trong bộ nhớ cache trước khi sử dụng, nhưng nó không bắt buộc trừ khi chỉ thị bổ sung must-revalidateđược chỉ định.
Frédéric

Để hoàn thiện, chỉ thị tối thiểu và phù hợp hơn là no-cachevẫn cho phép lưu vào bộ đệm nhưng bắt buộc phải xác nhận lại trên máy chủ gốc trước khi sử dụng. Để tránh bộ nhớ đệm được xác nhận lại, bạn phải thêm no-storecùng với no-cache. ( no-storemột mình hoàn toàn sai vì bộ nhớ cache dễ bay hơi được phép lưu trữ nội dung được đánh dấu là no-store.)
Frédéric

4

Đối với MVC6 ( DNX ), không cóSystem.Web.OutputCacheAttribute

Lưu ý: khi bạn đặt NoStoretham số Thời lượng không được xem xét. Có thể đặt thời lượng ban đầu cho lần đăng ký đầu tiên và ghi đè lên điều này bằng các thuộc tính tùy chỉnh.

Nhưng chúng ta có Microsoft.AspNet.Mvc.Filters.ResponseCacheFilter

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

Có thể ghi đè bộ lọc ban đầu bằng thuộc tính tùy chỉnh

    [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);
        }
    }

Đây là một trường hợp sử dụng

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

1

Bộ đệm đầu ra trong MVC

[OutputCache (NoStore = true, Thời gian = 0, Vị trí = "Không", VaryByParam = "*")]

HOẶC LÀ
[OutputCache (NoStore = true, Thời gian = 0, VaryByParam = "Không")]


Xem các bình luận khác ( 1 , 2 , 3 ) về nhiều câu trả lời đã gợi ý bằng cách sử dụng này. Dòng thứ hai của bạn sai và sẽ dẫn đến sự cố với một số trình duyệt.
Frédéric


0

Giải pháp ASP.NET MVC 5:

  1. Bộ nhớ đệm đang ngăn chặn tại một vị trí trung tâm: các App_Start/FilterConfig.cs's RegisterGlobalFiltersphương pháp:
    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. Khi bạn đã hiểu điều đó, sự hiểu biết của tôi là bạn có thể ghi đè bộ lọc toàn cầu bằng cách áp dụng một OutputCachechỉ thị khác ở cấp Controllerhoặc Viewcấp. Đối với bộ điều khiển thông thường, nó
[OutputCache(NoStore = true, Duration = 0, Location=System.Web.UI.ResponseCacheLocation.None, VaryByParam = "*")]

hoặc nếu nó là ApiControllernó sẽ là

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, Location = System.Web.UI.OutputCacheLocation.None, VaryByParam = "*")]
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.