Html5 Offline Cache Integration with ASP.NET MVC Bundle
One of the lesser known and misunderstood feature of Html5 is offline cache, with carefully crafted it can make your application blazingly fast. In this post, I will show you how can you implement offline cache with the new asp.net asset bundle, the same technique can be also applied to other asset managers like combres or cassette. Currently the browser support of offline cache is aligned towards mobile platform comparing to desktop(except IE all other has this support) but the nice thing of offline cache is the browser that does not support it will simply ignore it, on the other hand the mobile platform which is dominated by the mobile web-kit has first class support of it, so I will use the default jQuery Mobile project the comes with the ASP.NET MVC 4.
Before digging into the code, let me give you a brief description of the offline cache. There are two steps to enable offline caching, first you have to write a manifest file which contains the rules of the caching and next you have to include the file in your html page. The manifest file has three sections a) cache b)network and c) fallback. The cache section is used to specify the list of urls that would be downloaded by the browser. The network section contains the list urls that would always be fetched from the server, which means they will not be downloaded for offline viewing. The last section fallback contains a map of online and offline urls, if the device is offline then the offline version of the url will be served for the give online url. In a typical cache manifest the fallback section is not specified and the network section contains a asterisk (*) which files that urls that are not specified in the cache will be consulted with the server. For example:
You can also include urls form another domain unless your application is not hosted under ssl. Next, you need to do is include the manifest file in the html, like the following:
Now, we have everything setup the next and most important thing is cache invalidation, whenever we update a file we need to inform the client to download it again. This is done by adding a version number in the manifest file as comment.
Lets see how we can implement it in ASP.NET MVC, well there are many way to implement it but I preferred to create a dedicated ActionResult. The action result should have argument for each section in the constructor, except the cached assets everything else should be optional. When executing the action result there are three important thing that we have to consider, first the manifest file mime-type must be text/cache-manifest, it should not be cached by the browser and last it's encoding must be in utf-8.
Next, we have to create a controller which returns this action result:
There are two important things in the above code, first I am specifying false as the second argument of the ResolveBundleUrl call, which means it should not generate the hash as query string of the bundle url, caching based upon query string is not a good practice (I hope the ASP.NET bundle will change the implementation prior the final release), next I am using a custom extension method which extracts the hash of the bundle as use it as versioning of the cache manifest file. The implementation of this extension method is trivial:
Next, change the view to include the manifest:
and a route:
Now, if you run it you may not be able to follow some of the status message because it will happen so fast, so lets add some artificial delay in the code:
And the controller that does the delay:
Now when you run it with you will find the following status messages:
If you open the chorme dev tools and navigate to the resource section, you will find the cached resources:
Lets see how it would behave when the manifest file is update, lets add another delay which modifies the manifest file:
Now, if you run it again you will find the following behavior:
If you run it FireFox, it will prompt you whether to store data in the offline storage, just click allow, this is weird behavior of FireFox, I hope this will get addressed in future version of Firefox. I find chorme gives the best experience when it comes to develop html5 related features.