2

I'd like to set the Cache-Control header to public in my ASP.NET (MVC) app. The problem is that there is code (that I can't change) that previously set the cache policy like this:

        var response = htmlHelper.ViewContext.HttpContext.Response;
        response.Cache.SetExpires(System.DateTime.UtcNow.AddDays(-1));
        response.Cache.SetValidUntilExpires(false);
        response.Cache.SetRevalidation(HttpCacheRevalidation.AllCaches);
        response.Cache.SetCacheability(HttpCacheability.NoCache);
        response.Cache.SetNoStore();

I can't find a way to override this later, because no matter how I try to set cache control, the above will take effect. E.g. neither of the following can counter caching being disabled previously:

        httpContext.Response.Headers["Cache-Control"] = "public";
        var cache = httpContext.Response.Cache;
        cache.SetExpires(cacheItem.ValidUntilUtc);
        cache.SetValidUntilExpires(true);
        cache.SetRevalidation(HttpCacheRevalidation.None);
        cache.SetCacheability(HttpCacheability.Public);
        cache.SetMaxAge(cacheItem.ValidUntilUtc - _clock.UtcNow);

Is there a way to somehow override or reset the cache policy?

Piedone
  • 2,693
  • 2
  • 24
  • 43

2 Answers2

7

Apparently this is not simply possible, as HttpCachePolicy will actively prevent you from setting a "higher" cachability, i.e. if you try to set Public after NoCache was set, nothing will happen.

It seems the only, hackish way is to use private reflection and invoke the internal Reset method as following:

        var cache = httpContext.Response.Cache;
        var cachePolicy = (HttpCachePolicy)typeof(HttpCachePolicyWrapper).InvokeMember("_httpCachePolicy", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.GetField, null, cache, null);
        typeof(HttpCachePolicy).InvokeMember("Reset", BindingFlags.Instance | BindingFlags.NonPublic | BindingFlags.InvokeMethod, null, cachePolicy, null);
        cache.SetCacheability(HttpCacheability.Public);
Piedone
  • 2,693
  • 2
  • 24
  • 43
0

In your first code sample, it looks like the caching properties are being set in an HTML Helper which is most likely rendered on a view somehow. I'm guessing your attempting to override the properties is happening too early then being overwritten on the rendering of the view.

Obviously, changing the code that sets these headers is best. If you absolutely cannot, I see a few routes forward:

  1. Don't return the view from the controller immediately. You may have something like:

    public ActionResult MyAction() {
        // code here
    
        return View();
    }
    

    turn that into so you can then attempt to override caching in the view:

    public ActionResult MyAction() {
        // code here
    
        var result = View();
        ResetCaching();
        return result;
    }
    
  2. If that doesn't work, use OnResultExecuted or OnActionExecuted in the controller to catch when the view has completed executing or the action has executed and attempt to override the caching after the view and/or action has executed but before the controller is disposed of.

    Instead of overriding the actual events on the controller, you could implement a custom action filter which could keep the code cleaner and allow you to reuse it in multiple places.

  3. Absolute worst case, use the Application_PreSendRequestHeaders event in the global.asax file and if the request meets the requirements of having the caching reset, modify the headers right before they are sent to the browser. This method will cause you to inspect every single request that passes through your application.

Steven V
  • 16,357
  • 3
  • 63
  • 76
  • Thank you, but this is not the issue. The order is correct, i.e. my code runs after the code causing the problem (it's actually executed in a result filter in OnResultExecuted). What causes my attempts to fail is that once set to NoCache HttpCachePolicyBase won't allow you to enable caching. – Piedone Apr 23 '15 at 15:17
  • @Piedone I didn't know it prevented you from resetting it, sorry about that. After reading the source of [HttpCachePolicy](http://referencesource.microsoft.com/#System.Web/HttpCachePolicy.cs) I don't see an official way to reset it. [This answer](http://stackoverflow.com/a/28445308/254973) does show you how to tinker with the internal properties of the policy to reset things if you're interested in using reflection. – Steven V Apr 23 '15 at 15:40
  • Thank you, yes, that's what I ended up doing. Will add an answer. – Piedone Apr 23 '15 at 15:41