This is the eleventh part of Building ASP.Net Web API RESTful Service Series. The topics we’ll cover are:
- Building the Database Model using Entity Framework Code First – Part 1.
- Applying the Repository Pattern for the Data Access Layer – Part 2.
- Getting started with ASP.Net Web API – Part 3.
- Implement Model Factory, Dependency Injection and Configuring Formatters – Part 4.
- Implement HTTP actions POST, PUT, and DELETE In Web API – Part 5.
- Implement Resources Association – Part 6.
- Implement Resources Pagination – Part 7.
- Securing Web API – Part 8.
- Preparing Web API for Versioning – Part 9.
- Different techniques to Implement Versioning – Part 10.
- Caching resources using CacheCow and ETag – Part 11. (This Post)
Update (2014-March-5) Two new posts which cover ASP.Net Web API 2 new features:
Caching resources using CacheCow and ETag
In this post we’ll discuss how we can implement resource caching by using an open source framework for HTTP caching on the client and server, this framework is called CacheCow. It is created by Ali Kheyrollahi, we’ll cover in this post the server side caching.
API Source code is available on GitHub.
Using resource caching will enhance API performance, reduce the overhead on the server, and minimize the response size.
The form of caching we want to achieve here called Conditional Requests, the idea is pretty simple; the client will ask the server if it has an updated copy of the resource by sending some information about the cached resources it holds using a request header called ETag, then the server will decide weather there is an updated resource that should be returned to the client, or the client has the most recent copy, so if the client has the most recent copy the server will return HTTP status 304 (Not modified) without the resource content (empty body). By using Conditional Requests the client will send HTTP requests always to the server but the added value here that the server will only return full response 200 (OK) including resource content within the body only if the client has stale copy of the resource.
So what is ETag (Entity Tag)?
ETag is a unique key (string) generated at the server for particular resource, you can think of it as check-sum for a resource that semantically changes when the resource has updated.
ETag has two types, weak, and strong. The value of weak ETag is prefixed by W, i.e. ETag: “W/53fsfsd322″ and the strong ETag is not prefixed with anything, i.e. ETag: “534928jhfr4”. usually Weak ETags indicates that the cached resource is useful to be used for short time (In memory caching) and the strong ETag means that the resource caching is implemented using persistence storage and the content of the both resources (client and server) are identical byte for byte.
How ETags work?
By looking on the illustration below we will notice that at the beginning the client initiate HTTP GET request asking for the course with Id: 4 assuming that that this resource has not requested before, then the server will respond by returning the resource in the response body along with a generated ETag in the response header.
Now the client wants to request the same resource again (Course id: 4) by sending another HTTP GET, assuming that the client is interested in using caching, then the GET request initiated will include a header called If-None-Match with the value of the ETag for this resource, once the server receives this request it will read the ETag value and compare it with the ETag value saved on the server, if they are identical then the server will send HTTP status 304 (Not modified) without the resource content (empty body) and the client will know that it has the most recent copy of the resource.
For HTTP GET and DELETE we can use the header If-None-Match, but when updating a resource and we want to use ETags we have to send the header If-Match with the HTTP PUT/PATCH request, so the server will examine the ETag and if they are different; the server will respond with HTTP Status 412 (Precondition Failed) so the client knows that there is a fresher version of the resource on the server and the update won’t take place until the client has the same resource version on the server.
Configuring Web API to use CacheCow
After we had a brief explanation of how conditional requests and ETags work let’s implement this feature in our Web API.
Now we need to Install CacheCow server from NuGet, so open NuGet package manager console and type “Install-Package CacheCow.Server -Version 0.4.12” this will install two dlls, the server version and common dll.
Configuring CacheCow is pretty easy, all we need to do is to create Cache Handler and inject it into the Web API pipeline, this this handler will be responsible to inspect each request and response coming to our API by looking for ETags and Match headers and do the heavy lifting for us.
To implement this open the file “WebApiConfig.cs” and at the end of the file add the below line of codes:
1 2 3 |
//Configure HTTP Caching using Entity Tags (ETags) var cacheCowCacheHandler = new CacheCow.Server.CachingHandler(); config.MessageHandlers.Add(cacheCowCacheHandler); |
Till this moment we have configured our API to use In-memory caching; this is the default configuration for CacheCow, this will be fine if you have a single server or for demo purposes, but in case of load balancers and web farms, the cache state should be persisted for the whole farm in single place and all web servers should be aware of this cache. In this case we need to configure CaschCow to use persistence medium (SQL Server, MongoDB, MemCache). But before moving to persistence store let we test the in-memory caching.
Cache Cow in-memory caching:
To test this we need to open fiddler and choose the Composer tab, we’ll issue a GET request to the URI: http://localhost:{your_port}/api/courses/4 The request will look as the image below:
In this GET request we need to note the following:
- The HTTP status response is 200 OK which means that server returned the resource content in the response body.
- New two headers are added to the response which they are eTag and Last-Modified, we are interested here on the eTag value and we’ll get rid off the Last-Modified header later on as we’ll send conditional requests using eTag only.
- The value of the eTag is weak (Prefixed with W) because we are using in-memory caching for now, in other words if we restarted IIS or shutdown its worker process all eTag values the client saving are useless.
Now after receiving the eTag header value, the client is responsible to send this value along with any subsequent requests for the same resource, the client will use the header “If-None-Match” when sending the request and the server will respond by HTTP status 304 with empty response body if the requested resource has not changed, other wise it will return 200 OK with new eTag value and resource content in the response body. To examine this let we open fiddler and issue GET request to the same resource (Course with Id: 4) as the image below:
In this GET request we need to note the following:
- The HTTP status response is 304 Not modified which means that server returned empty body and the copy of the resource at client side is the latest.
- The same eTag header value is returned in the response.
Now let’s move to configure CacheCow to use persistence caching medium (SQL Server) instead of in-memory caching.
Cache Cow persistence caching using SQL Server:
Configuring CacheCow to use SQL server is easy we need to install NuGet package which works with the medium we want to use, in our case SQL server, so open NuGet package manager console and type: “Install-Package CacheCow.Server.EntityTagStore.SqlServer -Version 0.4.11“, this will install the requiered dlls.
Now open the file “WebApiConfig.cs” again paste the code snippet below:
1 2 3 4 5 6 |
//Configure HTTP Caching using Entity Tags (ETags) var connString = System.Configuration.ConfigurationManager.ConnectionStrings["eLearningConnection"].ConnectionString; var eTagStore = new CacheCow.Server.EntityTagStore.SqlServer.SqlServerEntityTagStore(connString); var cacheCowCacheHandler = new CacheCow.Server.CachingHandler(eTagStore); cacheCowCacheHandler.AddLastModifiedHeader = false; config.MessageHandlers.Add(cacheCowCacheHandler); |
What we implemented above is obvious, CacheCow needs to store caching information in SQL server, so we need to inform which database to use, in our case we’ll put this caching information in the same database of the API “eLearning”. Then we need to pass the SQL server eTagStore instance to the caching handler. As well we’ve turned off adding last modified header to the response because we are interested in eTag values only.
If you directly tried to issue any request against our API you will receive 500 error (Internal Server Error) because as we mentioned before CacheCow needs to store information about the cached resource on the server, this means it needs a table and some stored procedures to manipulate this table, so we need run SQL script before we go on. To do this navigate to the path where you NuGet packages are installed, usually they are on “{projectpath}packagesCacheCow.Server.EntityTagStore.SqlServer.0.4.11scripts“, open the file named “script.sql” and execute all its content against your local eLearning Database.
After running the script navigate to SQL server explorer and open eLearning database, you will notice that this script created a table named “CacheState” and another five stored procedures mainly used for manipulating this table.
To test this out we need to issue the same GET request as the image below:
As you notice from the above image, the ETag value now is not weak, it is strong because we persisted it on SQL server, so if we opened the table “CacheState” you will notice that CacheCow has inserted new record including the ETag value, Route Pattern, Last Modified date, and binary hash key as the image below:
Now if the client sends any subsequent requests to the same resource, the same ETag value will be returned as long as no other clients updated the resource by issuing HTTP PUT/PATCH on the same resource.
So if the client includes this ETag value within the If-None-Match header then the server will respond by 304 HTTP status.
Now let’s simulate the update scenario, the client will issue HTTP PUT on the resource (Course with Id 4) including the ETag value in the header If-Match along with the request as the image below:
In this PUT request we need to note the following:
- The HTTP status response is 200 OK which means that client has the most recent copy of the resource and the update of the resource has took place successfully.
- New ETag has been returned in the response because the resource has been changed on the server, so the client is responsible to save this new ETag for any subsequent requests for the same resource.
- The new ETag value and last modified date has been updated in table “CacheStore” for this resource.
Now if we directly tried to issue another PUT request using the old ETag (8fad5904b1a749e1b99bc3f5602e042b) we will receive HTTP Status 412 (Precondition Failed) which means that updating the resource has failed because the client doesn’t have the latest version of the resource. Now client needs to issue GET request to get the latest version of the resource and the new generated ETag (ad508f75c5144729a1563a4363a7a158), let’s test this as the image below:
That’s all for now!
I hope you liked this tutorial, I tried my best to explain all important features of Web API which allows you to get started and build your RESTful API. You can always get back to the complete running source code on github.
Please feel free to drop any comment or suggestion to enhance this tutorial. if you have quick question please send me on twitter.
As I can see, cashing with e-tags is helpfull for not very deep urls. Assume,you do a get request: /api/courses and recieve list of courses and e-tag for this. Than you do a post to /api/courses/4 for example. The list of courses changed, but getting another request for /api/courses will give you an old value. Is there a method to say to Cachecow to renew e-tags for some resourses?
Thanks for your feedback, I agree with you, I wouldn’t turn on caching for a huge list that will change frequently, I’ll keep it for individual resources.
I tried to turn off caching on specific routes i.e(/api/courses/) and keep it on single course (/api/courses/4) using AttributeBasedCacheControlPolicy with no luck. I asked CacheCow creator (@aliostad) on how to implement this. I’ll keep you posted.
I’ll give you another example. Lets say /api/blogs gives us a list of blogs [e.g. Repository.GetAll(…)] and /api/blogs/10_01_14 will give a blog with comments and replies with one request to database [e.g. Repository.Include(b=>b.Comments).GetSingle(…)]. I don’t think blogs are written every day, but comments… So caching the list of blogs is better, but you may update your blog doing Post to /api/blogs/10_01_14, so e-tag for the list (/api/blogs) in some cases must be changed.
@tjoudeh Say there is a list of countries that I want to fetch .. GET api/countries GET api/states/?countryid=1
Here data entry happens in normal MVC views. So, PUT/POST will not be called through APIs. Will it still update?
This has been for a while, but you need to do updates through API so SPs get executed, other than this it wont work.
Great article! It will be interesting to see how to work with etags in AngularJS. How to save them to cookies, best practices. And how to solve problem in my first comment. Thanx for this great work you do!
Thank you, I wish I’ve more time to blog about this. But will keep it on my soon to-do list 🙂
Thanks for the article. Good to see how easy it is to do with CacheCow. Btw, W/ has nothing to do with storage? W/ means semantically equivalent and strong means byte-per-byte identical. I just recently implemented HTTP caching as well using ActionFilterAttribute. The idea is I can control which resources should be cached in controller. However, I still believe for example api/courses/4 should not be a good candidate for cache otherwise we could end up with for example api/courses/100001 with 100001 records in db/memory. api/courses is indeed yes to save bandwidth and data transfer. Here is my implementation of it, certainly may not be as good as CacheCow but maybe useful in some scenarios. http://weaklinglifter.blogspot.com/2014/02/webapi101-http-caching-on-action.html
regards,
stevanuz
Hi guy,
I’ve read your articles “Building ASP.Net Web API RESTful Service Series” and “Tutorial for Building SPA using AngularJS”. Both of them are very useful. Thank for sharing it.
It would be fantastic if you could demonstrate how authenticate in AngularJS app using WebApi like you showed in this article.
regards,
Anderson
Thanks Anderson, I’ll consider blog about this soon.
I go through all the steps of making this web Api but when i run it and request for api/Courses
than the constructor of baseApiController class is not invoking.
Strange, what you can do is to fork my working project on Github and compare it your working one, double check your routing configuration on class WebApiConfig.cs
excellent example!!! very complete!, If you ever find a solution for api/courses Etag from been invalidated when any courses change, let us know. by the way… any suggestions in how to implement other authentications like oAuth?
Hello Angel, glad you like it. I bought recently this book and there is detailed chapter covers oAuth and different authentication techniques, will keep you posted once I read it.
http://www.amazon.com/Pro-ASP-NET-Web-API-Services/dp/1430247258
Hi Angel,
I know its too late now, but if you are still interested you ca check my new 4 parts detailed posts about Adding oAuth to Web API project.
Your article is “the best” compared to other examples. Very detailed and industry standard implementation. Keep it up,
Thanks
Roopesh
Thank you Roopesh, glad to hear this. It is my pleasure to write posts benefit the community 🙂
Your series are very useful, It save my life. Thank you very much.
Glad that you liked them, thanks for taking the time to comment 🙂
I have added CacheCow.Server Package and as you added the line in
public static void Register(HttpConfiguration config)
{
// Web API configuration and services
// Web API routes
config.MapHttpAttributeRoutes();
config.Routes.MapHttpRoute(
name: “DefaultApi”,
routeTemplate: “api/{controller}/{id}”,
defaults: new { id = RouteParameter.Optional }
);
var cacheCowCacheHandler = new CacheCow.Server.CachingHandler();
config.MessageHandlers.Add(cacheCowCacheHandler);
}
but line var cacheCowCacheHandler = new CacheCow.Server.CachingHandler();
is showing error:Error 2 ‘CacheCow.Server.CachingHandler’ does not contain a constructor that takes 0 arguments
Hi Devesh,
This has been for a while, I tell what is the exact issue now, but I recommend you to install the CacheCow.Server package using the same version I used in this project, most probably you’ve installed newer version which introduced breaking changes. To know the version installed open the file packages.config in my github and use this exact package.
Hope this help.
Do let me know how does cache cow gets to know whether the resource is updated. I mean assuming our storage is SQL server like RDBMS, there is an update to the particular resource in rdbms. Unless server side code is executed, the application does not know about update on resource. Cache cow seems very similar outputCaching except that there is client negotiation is invoved. Is cache cow configured to verify whether the resource is getting updated?
I just verified and got to know that cache cow verifies the http verbs like put/post/delete and based on that it gets info on updated resource . But what if there is an RPC style call that may not use exact http acton method on the resource but may update resource info in the RDBMS. In that case does cache cow gets to know whether the resource got updated
Nice resource!
I was happy to get up and running in few minutes but my cache doesn’t seem to be invalidated after put. I have simple webapi with attribute routing that is api/tenants and I want the list to be updated after PUT is issued to update api/tenant/1 , for example. Did you find a way to invalidate the list?
Here is what I have in my startup config:
var etagStore = new SqlServerEntityTagStore(connString);
var cacheHandler = new CachingHandler(config, etagStore);
config.MessageHandlers.Add(cacheHandler);
This had been for a while 🙂 So I really do not have clear answer now, but I recommend you to open issue with GitHub author, he should have better answer than me.
Just want you to thank for all your Web API tutorial series. 🙂 🙂 🙂 Keep this blog up!!!
You are welcome, glad you liked them!
I thoroughly enjoyed this tutorials which gives makes the better understanding of the basics. Great going 🙂
Thank you, always happy to help!
Thanks for the nice article.