Jose Romaniello wrote a very nice blog post about to handle HTTP cache validation with ETags and the WCF Web APIs. For the ETags server implementation there are two different aspects to be considered: the code that checks if a resource has been modified based on the incoming request ETag value (this is handled by a DelegatingChannel in Jose’s implementation), and the part where the resource ETag is added to the response so the client can use this value to make further requests (In Jose’s implementation this is handled on case by case basis in every resource handler). I was interested to see if the second part could be handled in a generic and automated way by using some conventions, so the custom logic in the resource handler could be spared. So let’s get started!
First of all, let’s define the resource.
As in Jose’s post, for the ETag value we can use the DB version field for example. The resource handler goes as follows.
The EntityTagResponseHandlerChannel handles the ETag injection to the responses. On top of that, when a PUT or DELETE request succeeds, the channel removes the ETag associated to the incoming url from the cache. This ensures that further GET request won’t get a stale version of the resource.
Note that we check if the ETag has been previously been set (line 37) meaning that custom ETag handling logic in the resource handler overrides the Channel logic.
The tricky part here is to get the ETag value from the resource associated to the response. The object contained in the response variable is a generic HttpResponseMessage<TResource> that contains the original resource. The problem is that, as the DelegatingChannels are non-generic objects, they can only reference the non-generic versions of the response entities (HttpResponseMessage, ObjectContent base classes) which don’t have access to the original resource object. Since casting to the generic version is out of the question (this Channel should handle any resource type request), we take advantage of a dynamic variable to execute the generic ReadAs method that gives us the original resource entity (line 40).
Finally the ETagProvider retrieves the ETag from the resource object if exists.
The ETag property is retrieved by using Reflection and a dictionary of PropertyInfos by type is used to improve performance.
HTTP cache expiration could be implemented in a similar way by using for example a .NET attribute defining the resource MaxAge, then a DelegatingChannel would grab this value and add it to the associated response.
Nevertheless, bear in mind that this is by no means production-ready code! My main purpose with this post was to show how the Wcf Web Apis infrastructure can be leveraged to implement HTTP protocol features such as cache validation.