JavaScript Developer
JavaScript Developer

Reputation: 4008

Prevent Caching in ASP.NET MVC for specific actions using an attribute

I have an ASP.NET MVC 3 application. This application requests records through jQuery. jQuery calls back to a controller action that returns results in JSON format. I have not been able to prove this, but I'm concerned that my data may be getting cached.

I only want the caching to be applied to specific actions, not for all actions.

Is there an attribute that I can put on an action to ensure that the data does not get cached? If not, how do I ensure that the browser gets a new set of records each time, instead of a cached set?

Upvotes: 212

Views: 211910

Answers (9)

Jaguir
Jaguir

Reputation: 3680

You can use the built-in cache attribute to prevent caching.

For .NET Framework: [OutputCache(NoStore = true, Duration = 0)]

For .NET Core: [ResponseCache(NoStore = true, Duration = 0)]

Be aware that it is impossible to force the browser to disable caching. The best you can do is provide suggestions that most browsers will honor, usually in the form of headers or meta tags. This decorator attribute will disable server caching and also add this header: Cache-Control: public, no-store, max-age=0. It does not add meta tags. If desired, those can be added manually in the view.

Additionally, jQuery and other client frameworks will attempt to trick the browser into not using its cached version of a resource by adding stuff to the url, like a timestamp or GUID. This is effective in making the browser ask for the resource again but doesn't really prevent caching.

On a final note. You should be aware that resources can also be cached in between the server and client. ISP's, proxies, and other network devices also cache resources and they often use internal rules without looking at the actual resource. There isn't much you can do about these. The good news is that they typically cache for shorter time frames, like seconds or minutes.

Upvotes: 276

Csaba Toth
Csaba Toth

Reputation: 10697

ASP.NET MVC 5 solutions:

  1. Caching prevention code at a central location: the App_Start/FilterConfig.cs's RegisterGlobalFilters method:
    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. Once you have that in place my understanding is that you can override the global filter by applying a different OutputCache directive at Controller or View level. For regular Controller it's
[OutputCache(NoStore = true, Duration = 0, Location=System.Web.UI.ResponseCacheLocation.None, VaryByParam = "*")]

or if it's an ApiController it'd be

[System.Web.Mvc.OutputCache(NoStore = true, Duration = 0, Location = System.Web.UI.OutputCacheLocation.None, VaryByParam = "*")]

Upvotes: 4

Davut Gürbüz
Davut Gürbüz

Reputation: 5716

For MVC6 (DNX), there is no System.Web.OutputCacheAttribute

Note: when you set NoStore Duration parameter is not considered. It is possible to set an initial duration for first registration and override this with custom attributes.

But we have Microsoft.AspNet.Mvc.Filters.ResponseCacheFilter

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

It is possible to override initial filter with a custom attribute

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

Here is a use case

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

Upvotes: 4

Nenad
Nenad

Reputation: 26637

Correct attribute value for Asp.Net MVC Core to prevent browser caching (including Internet Explorer 11) is:

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

as described in Microsoft documentation:

Response caching in ASP.NET Core - NoStore and Location.None

Upvotes: 2

Deepak
Deepak

Reputation: 119

Output Caching in MVC

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

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

Upvotes: -2

Mathew Thompson
Mathew Thompson

Reputation: 56429

To ensure that JQuery isn't caching the results, on your ajax methods, put the following:

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

Or to prevent caching in MVC, we created our own attribute, you could do the same. Here's our code:

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

Then just decorate your controller with [NoCache]. OR to do it for all you could just put the attribute on the class of the base class that you inherit your controllers from (if you have one) like we have here:

[NoCache]
public class ControllerBase : Controller, IControllerBase

You can also decorate some of the actions with this attribute if you need them to be non-cacheable, instead of decorating the whole controller.

If your class or action didn't have NoCache when it was rendered in your browser and you want to check it's working, remember that after compiling the changes you need to do a "hard refresh" (Ctrl+F5) in your browser. Until you do so, your browser will keep the old cached version, and won't refresh it with a "normal refresh" (F5).

Upvotes: 318

Konamiman
Konamiman

Reputation: 50273

Here's the NoCache attribute proposed by mattytommo, simplified by using the information from Chris Moschini's answer:

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

Upvotes: 5

Chris Moschini
Chris Moschini

Reputation: 37947

All you need is:

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

or, if you want to disable it for an entire Controller:

[OutputCache(Duration=0)]
public class MyController

Despite the debate in comments here, this is enough to disable browser caching - this causes ASP.Net to emit response headers that tell the browser the document expires immediately:

OutputCache Duration=0 Response Headers: max-age=0, s-maxage=0

Upvotes: 50

dfortun
dfortun

Reputation: 764

In the controller action append to the header the following lines

    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.

Upvotes: 19

Related Questions