Posts Tagged 'Action Filters'

Output Caching and Authenticated Users

In a recent post, I looked at using a custom parameter with OutputCache to provide different versions of an ASP.NET MVC view to different clients (mobile/traditional devices, AJAX / no-AJAX clients). The one question left unaddressed, however, was: what if there are some circumstances where you don’t want to cache at all?

Output caching means that your code runs once to provide the output, then doesn’t run again until the caching period has expired. In my example, that meant it would run four times for my different circumstances, but not again for each individual circumstance until the timeout had expired. But it turns out that isn’t sufficient to my needs. If the user is logged in, the code needs to run every time.

CocktailsRUs is a community site. Anyone can join, and when they join, they can mark cocktails as favorites for ease of access. Every time they view a cocktail, they can choose to add it to (or remove it from) their favorites, and thus be presented with the appropriate button to add or remove a favorite.

screengrab of remove favorite button

So how do we stop the site from using output caching with authenticated users?

One quick and (very) dirty approach would be to use the GetVaryByCustomString() method to assign a unique value (a timestamp or a GUID) as the cache key for authenticated users. That would ensure the code runs every time, but would also lead to a plethora of unwanted pages in the cache.

Fortunately, there is a much better solution. The Output Cache exposes a callback that allows you to decide whether to return the cached item, or run the code. We can use this callback to determine if the user is authenticated–and if they are, invalidate the cache for this response.

The first thing we need to do is create our own derived version of the Output Cache. That’s straightforward–just inherit from OutputCacheAttribute like so:

inheritance code sample

Then we need to override the OnActionExecuting() method to set up the callback where we will invalidate the cache:

OnActionExecuting code sample

Now all we need to do is implement our OnlyIfAnonymous() method and tell it not to cache if this is an authenticated user. The signature of AddValidationCallback gives us access to the HttpContext, an optional data object (which we don’t need) and the HttpValidationStatus–which is passed in by reference and allows us to ignore the cache for this request. Here is the completed method:

Callback code sample

The one remaining step is to replace the OutputCache directive on our controller method with a reference to our new OnlyUnAuthenticated attribute.

Attribute code sample

Now we have a solution that caches appropriately for anonymous users, but ensures that the code runs every time for authenticated users.

Kevin Rattan

For related information, check out these courses from Learning Tree:

Building Web Applications with ASP.NET MVC

Building Web Applications with ASP.NET and AJAX

Preventing Duplicate Logins in ASP.NET

ASP.NET provides a convenient and highly extensible authentication and authorization system that is shared by both ASP.NET MVC and ASP.NET Web Forms. I have cheerfully used it for years, without ever worrying about the fact that there is nothing in the system to prevent the same user from logging in multiple times. The other day, though, I received an email from a former student who was concerned about just that. Their business rules had changed, and now they needed the following behavior: if a user logs on twice with the same identity, the first user should be automatically logged out.

It was not something I had ever thought about before, and I became intrigued. I could imagine preventing the second user from logging in – but how to allow the second login to go through, and bounce the first user from the site? The idea intrigued me so much, I decided to put together a little test site.

I created a standard ASP.NET MVC3 Web application.

I then added code to the LogOn action method:

(The reason for the Session (“fixer”) is that if nothing is in the session, the ID can change between requests – and we’re relying on it staying the same).

Now that we have saved the sessionID of the current user (and overwritten any existing value), we can test for it on every request – and bounce out any user who doesn’t have the current one. In ASP.NET MVC, this is best done using an action filter. Here is the code for the LogUserOut action filter:

Now all we need to do is create an action method and View for Bounce, and assign the LogUserOut attribute to the appropriate action methods – in this case, Index and About:

Then I tested it. I logged on in Internet Explorer, created a new user and had no problem clicking Home and About.

Then I opened Firefox and logged in as the same user. Again, this was no problem and everything worked as it should.

Then I went back and clicked on a link in IE – and was bounced out: exactly what was supposed to happen.

So I logged in again in using IE.. and then went back to Firefox, where the current user was once again bounced out.

So the principle works. I wasn’t that happy with the solution, though. The use of the UserName is less than optimum – what if two users had the same name? So I reworked it to use ProviderUserKey as the cache key. That added a little complexity, as the MembershipUser is not available inside the LogOn action. To get around the problem, I redirected to a new Welcome action after a successful login, and used that to add the user to the cache. I had to do a bit of work to ensure the returnUrl was still honored and that unauthenticated users didn’t hit the Welcome method and cause issues, but it was a better solution as a result. (In Web Forms, I would have used the LoggedIn event to access the MembershipUser. I would also have inherited from a base page to provide the bounce logic, rather than using an action filter.) Another possible change would be to use a database instead of the cache, which would be slower but more reliable in a Web farm.

If you’re interested in the code for the complete solution, then you can download it from here. If you want to find out more about ASP.NET MVC, you might want to check out Building Web Applications with ASP.NET MVC. If you’re interested I learning more about security and state management in Web Forms, then I’d recommend Building Web Applications with ASP.NET and Ajax (of which I’m the author).

Kevin Rattan

Learning Tree International

.NET & Visual Studio Courses

Learning Tree offers over 210 IT training and Management courses, including a full curriculum of .NET training courses.

Free White Papers

Questions on current IT or Management topics? Access our Complete Online Resource Library of over 65 White Papers, Articles and Podcasts

Enter your email address to subscribe to this blog and receive notifications of new posts by e-mail.

Join 29 other followers

Follow Learning Tree on Twitter


Do you need a customized .NET training solution delivered at your facility?

Last year Learning Tree held nearly 2,500 on-site training events worldwide. To find out more about hosting one at your location, click here for a free consultation.
Live, online training

%d bloggers like this: