Output caching in ASP.NET Web API

Today we will continue with our favorite topic – ASP.NET Web API. I’ve heard folks asking about how you could easily cache the output of the API methods. Well, in ASP.NET MVC, that’s dead easy, just decorate the Action with [OutputCache] attribute and that’s it. Unfortunately, ASP.NET Web API doesn’t have a built-in support for this attribute.

Which doesn’t mean you can’t have it. Let’s build one

Important update – January 2013 & November 2013

This article is outdated – I have since released a Web API CacheOutput, a Web API caching library available on GitHub. Follow this link to learn more and download. .

Alternatively, just use Nuget:

For Web API 2 (.NET 4.5) use:

For Web API 1 (.NET 4.0), you can still use the old one:

What are we going to do

We’ll build a class derived from ActionFilterAttribute, which will be responsible for:
– respond with an HttpResponseMessage built using data from Memory cache (preventing the data retrieval via Controller’s action body)
– if no data in Memory cache exists, saving there the data returned by the Controller’s Action body
– adding Cache Control directives, to govern caching on the client side
– allow caching to be toggled on and off for authenticated users
– cache only GET requests (purposly excluding other)

Coding – basic stuff

As usually, we start off with MVC4 project > WebAPI template. I recommend using either the latest source from Codeplex or the nightlies from NuGet (this project was built against the builds from 9th May).

So once you have all the latest stuff in GAC (sigh), we can proceed to coding. Let’s add class WebApiOutputCacheAttribute.cs and go from there.

We need to inherit from ActionFilterAttribute:

Let’s add a few private properties, that will be useful. The comments explain what they are for.

Three of them we will set via the constructor

Before we proceed to implementing the caching itself, let’s add two private helpers. First we will recognize whether or not the request should be cached

So in this case we check if both of the timespan variables have positive values, whether we should cache for authenticated users, and finally if we are dealing with a GET request.

The second private helper is below:

This is how we set the client side caching values, using Cache-Control in the Headers. We wrap this functionality into a separate method, since we’ll call it from two different places. More on that to come.

Coding – OnActionExecuting

Next step is to override OnActionExecuting base method. This is the method that executes prior to hitting the Controller’s action. This is precisely the place where we can:
1. check if there is some data in the cache, and if all caching conditions are met
2a. if so, return the appropriate HttpResponseMessage to the client
2b. if not, continue to the Controller a grab the data from where it should be coming from

If the request passes our caching rules, we build up a cache key. That has a format of [RequestUri.AbsolutePath:Request Content Type]. This way we will not accidentatlly serve a Response with wrong content type. Also, we will separately cache requests for individual objects i.e. Get(int id) and all such.

We will also need to store/retrieve the Response Content Type in/from the cache. that is because the Request and Response content types may not always be the same – for example by default (without injecting any custom formatters) if you request an ASP.NET Web API from the browser – “text/html” the response will come as “application/xml – so we don’t want to ruin that.

We check the Memory cache for the object (string, since the response content is just string), and if it’s there we we also try to get the response content type from the cache (as a fallback, we use Request Content Type). The key for retrieving the CT uses similar format – [RequestUri.AbsolutePath:Request Content Type:response-ct. Then we need to create a new Response instance, set the Content Type correctly and set the client cache directives according to what’s expected. Then we exit the method and this means the Controller’s action body never even executes.

Coding – OnActionExecuted

The final piece to our outputcache puzzle is overriding OnActionExecuted base method. This is invoked, as the name suggests, upon the completion of execution of the Controller’s action (method). We need to handle this event for situations in which we wish to populate the cache with some data (our code from above is only relevant when there is something in cache already).

We check if there isnt really anything in the cache using our cachekey – if not, we add both the Response body and the Response content type to the cache, using the key structure we agreed upon before. Finally, we set the client cache directives anyway, since they are independent from Memory caching and should be always part of our Response.

Usage

Now we can easily use our custom attribute inside our ApiControllers.

We can test this in Fiddler/browser (for client side caching) or VS debugger (for Memory caching) to see that indeed we are getting the cached response.

Source

As always, the source files are available for download, just like last time via gitHub.There is no point in including the entire solution, so I provide just the WebApiOutputCache.cs. Till next time my friends!
source on github