5
votes

I have a question on how to programmatically instruct ASP.NET to skip resolving a request from the output cache.

Imagine you've got a page output cached (e.g. http://domain/page.aspx) by means of applying cache policy settings from a CMS to the HttpResponse at runtime. On a per request basis depending on i.e. the current user is authenticated + a member of a set of known groups (or matched by business logic), I would like to instruct ASP.NET to skip resolving a request from the output cache.

The scenario is that two different users are on the system at the same time (or more). User A is authenticated + a member of a set of known groups, and user B is anonymous. Regardless of the page being output cached, I want the authenticated user to browse all pages as if no output caching was enabled - ever; at the same time, I would like ASP.NET to continue serving output cached pages to anonymous users (or, users who isn't matched by business logic).

The typical suggestion is to use VaryByHeader, VaryByParam etc. and pollute the output cache - not good, but while digging in the output cache module using Reflector, I noticed that the output cache module skips the current request in case a couple of known "cache-control" headers are present. As far as I'm concerned about headers, these are sent from the browser if the user forces a fresh copy to be rendered by hitting F5 or ENTER in the address bar.

So, what I'm doing is simply setting the "cache-control" header to "no-cache" in a custom http module in an event that goes before the ResolveRequestCache event to which the output cache subscribes. Like this:

context.Request.Headers["Cache-Control"] = "no-cache";

All is nice and dandy, however, if the HttpCachePolicy.SetValidUntilExpires(true) cache policy is set, ASP.NET disregards the request header previously set and serves the request from the output cache.

As an alternative, I guess I could write additional code in a post-processing event in the same http module to ensure that HttpCachePolicy.SetValidUntilExpires(false) is called, in case output caching has been configured, but I think it would be a more clean solution to actually be able to instruct ASP.NET to simply skip resolving the request from the output cache. I can imagine a lot of ackward solutions to this question, but I'm after the right one.

For reference, I have been trying most if not all relevant methods of the HttpCachePolicy class e.g.:

HttpResponse.Cache.SetNoServerCaching()).
2

2 Answers

5
votes

You can add an HttpCacheValidateHandler to your application in which you can implement any logic you want. It will be executed during the ResolveRequestCache event which is fired after Authentication and Authorization have been executed. The key is to return HttpValidationStatus.IgnoreThisRequest in the case where you want to bypass the Cache.

See this sample HttpModule for reference:

public class CacheModule : IHttpModule
{
    public void Init(HttpApplication context)
    {
        context.BeginRequest +=
            (s, e) => context.Context.Response.Cache
                        .AddValidationCallback(CacheHandler, null);
    }

    private static void CacheHandler(
        HttpContext context, object data,
        ref HttpValidationStatus validationstatus)
    {
        // bypass cache for all users with skipCache cookie
        validationstatus =
            context.Request.Cookies["skipCache"] != null
                ? HttpValidationStatus.IgnoreThisRequest
                : HttpValidationStatus.Valid;
    }

    public void Dispose()
    {
    }
}
0
votes

I'm not sure whether there is a supported method of doing this after something is cached.

But why don't you like the VaryByHeader option? Assuming there is a header you can look at to differentiate between logged in and not logged in users, that should work. It wouldn't pollute the cache if you have logic to only populate the output cache if the user is not logged in. Logged in users would alway get cache misses.