I and other contributors have been working on CacheCow for the last 8 months. I thought with a couple of posts I have explained the usage of CacheCow. But I now feel that with concept counts increasing, I need to start a series on CacheCow. Before doing that I am going to explain new concepts and the breaking change.
Breaking change
The breaking change was a change in the signature of CacheControlHeaderProvider from Func<HttpRequestMessage, CacheControlHeaderValue> to Func<HttpRequestMessage, HttpConfiguration, CacheControlHeaderValue> to accept HttpConfiguration.If you have provided your own CacheControlHeaderProvider, you need to provide HttpConfiguration as well - which should be very easy to fix whether web-host or self-host.
Cache Control Policy
So defining cache policy against resource have been by setting the value of CacheControlHeaderProvider which you would define whether a resource is cacheable and if it is, what is the expiry (and other related stuff):public Func<HttpRequestMessage, HttpConfiguration, CacheControlHeaderValue> CacheControlHeaderProvider { get; set; }
So by default CacheCow sets Func to return a header value for private caching with immediate expiry for all resources:CacheControlHeaderProvider = (request, cfg) => new CacheControlHeaderValue()
{
Private = true,
MustRevalidate = true,
NoTransform = true,
MaxAge = TimeSpan.Zero
};
Immediate expiry actually means that the client can use the expired resource as long as it validates the resource using a conditional GET - as explained before here.
But what if you want to individualise cache policy for each resource? We could use per-route handlers but that is not ideal and generally it depends on the resource organisation approach. I have explained in my previous post that resource organisation is one of the areas that needs to be looked at. But this is not within the scope of CacheCow. We are looking into solving this as part of another project while ASP.NET team are also looking into this. So I have decoupled the resource organisation project from CacheCow.
Having said that, in the meantime, I am going to provide some help with doing cache policy set up less painful. This means that CacheCow will come with a few pre-defined functions that help you with defining your cache control policy.
Good news! Cache policy definition using attributes
So now you can define your cache policy against your actions or controllers or both - although action attribute always takes precedence over controller. Using the popular ASP.NET Web API sample: [HttpCacheControlPolicy(true, 100)]
public class ValuesController : ApiController
{
public IEnumerable<string> Get()
{
return new[] { "cache", "cow" };
}
[HttpCacheControlPolicy(true, 120)]
public string Get(int id)
{
return "cache cow... mooowwwww";
}
So GET call to the first action (/api/Values) will have a max-age of 100 while GET to the second action (e.g. /api/Values/1) will return a max-age of 120.
In order to set this up, all you have to do is to set the CacheControlHeaderProvider property of your CachingHandler to GetCacheControl method of an instance of AttributeBasedCacheControlPolicy:
cachingHandler.CacheControlHeaderProvider = new AttributeBasedCacheControlPolicy(
new CacheControlHeaderValue()
{
NoStore = true
}).GetCacheControl;
So in above we have passed a default caching policy of no-caching. This table defines which attribute value (or default provided in the constructor) is used:
Cache Refresh Policy
CacheCow works best when HTTP API is actually a REST API. In other words, it uses uniform interface (i.e. HTTP Verbs) to modify resources and this means that the caching handler will get the opportunity to invalidate and remove the cache when POST, PUT, DELETE or PATCH is used.Problem is commonly HTTP API sits on the top of a legacy system where it has not control over modifications of resources and acts as a data provider. In such a case, the API will not be notified on resource changes and application will be responsible for removing cache metadata directly on the EntityTagStore used. And this is not always possible.
I am providing a solution for defining a time based cache refresh policy using attributes in a very similar fashion to Cache Control Policy - even the above table applies. Removal of items from cache store on the server happens upon the first request after the refresh interval has passed not immediately after interval. So we add the refresh policy provider:
cachingHandler.CacheRefreshPolicyProvider = new AttributeBasedCacheRefreshPolicy(TimeSpan.FromSeconds(5 * 60 * 60))
.GetCacheRefreshPolicy;
We have defined 5 hour refresh policy as default. And we override using controller or action attributes.








