5

Is there an easy way to cache ASP.NET whole page for anonymous users only (forms authentication used)?

Context: I'm making a website where pages displayed to anonymous users are mostly completely static, but the same pages displayed for logged-in users are not.

Of course I can do this by hand through code behind, but I thought there might be a better/easier/faster way.

Arseni Mourzenko
  • 50,338
  • 35
  • 112
  • 199
  • Did you ever get a resolution to this problem? We have a similar need that varyByCustom is not the answer for. – Nathan Palmer Feb 25 '10 at 19:38
  • Currently I use code-behind solution, where I can easily decide if I want to cache the page or not. If the user is not logged in, I cache. If the user is logged in, cache is disabled for a whole page (and caches only "static" parts instead). After I received the answer below, I searched for a solution without code-behind, but found nothing helpful. After all, a code-behind solution is also very clear and does not have major disadvantages over pure ASP.NET one. – Arseni Mourzenko Feb 26 '10 at 05:03
  • Could you share your codebehind solution? How do you control the cache like that? – Nathan Palmer Feb 26 '10 at 18:53
  • In code, if your page is cached by default, add something like that: if (User.Identity.IsAuthenticated) { Response.Cache.SetCacheability(HttpCacheability.NoCache); Response.Cache.SetExpires(DateTime.Now.AddMinutes(-1)); Response.Cache.SetNoStore(); } Or you can set cache in code if the user is not logged in and avoid setting anything on ASP.NET side. (Sorry for the ugly not indented code. Cannot find how to make newlines in comments...) – Arseni Mourzenko Mar 01 '10 at 00:49
  • @MainMa Have you seen this answer? http://stackoverflow.com/a/4951434/498892 – Alleo Aug 09 '12 at 17:13

3 Answers3

3

I'm using asp.net MVC, so I did this in my controller

if (User.Identity.IsAuthenticated) {
    Response.Cache.SetCacheability(HttpCacheability.NoCache);
    Response.Cache.SetExpires(DateTime.Now.AddMinutes(-1));
    Response.Cache.SetNoStore();
    Response.Cache.SetNoServerCaching();
}
else {
    Response.Cache.VaryByParams["id"] = true; // this is a details page
    Response.Cache.SetVaryByCustom("username"); // see global.asax.cs GetVaryByCustomString()
    Response.Cache.SetExpires(DateTime.Now.AddSeconds(60));
    Response.Cache.SetCacheability(HttpCacheability.Server);
    Response.Cache.SetValidUntilExpires(true);
}

The reason I did it this way (instead of declaratively) was I also needed the ability to turn it on and off via configuration (not shown here, but there's an extra check in the if for my config variable).

You still need the vary by username, else you'll not execute this code when a logged in user appears. My GetVaryByCustomString function returns "anonymous" when not authenticated or the users name when available.

WildJoe
  • 5,740
  • 3
  • 26
  • 30
1

You could use VaryByCustom, and use a key like username.

Bruno Reis
  • 37,201
  • 11
  • 119
  • 156
  • The problem with this is that it will cache pages displayed to logged-in users too. In my case, those pages are already partially cached, whereas some parts cannot be cached at all (even for the same user). Of course, I can set VaryByCustom key to a random value if user is logged in, but it will have huge performance issues. – Arseni Mourzenko Jan 17 '10 at 16:54
1

There is nothing stopping you from extending the existing attribute with the desired behaviour,

for example:

public class AnonymousOutputCacheAttribute : OutputCacheAttribute
{
    public override void OnActionExecuting(ActionExecutingContext filterContext) 
    {
       if(filterContext.HttpContext.User.Identity.IsAuthenticated)
          return;

        base.OnActionExecuting(filterContext);
    }  
}  

Haven't tested this, but I don't see reason why this shouldn't work.

Erti-Chris Eelmaa
  • 25,338
  • 6
  • 61
  • 78