Busting Kentico Cache

By Sterling Heibeck On January 12, 2017

Busting Kentico Cache
Back in the day it was okay to tell your clients to clear their browser cache to see new changes to cached client-side files on your website. But, it’s the 21st century and people expect things to just work, so cache busting is an issue that you need to be familiar with and you need to know how to solve it.

At BizStream we’ve started implementing Gulp on our projects, which allows us to bundle and minify our client-side assets. There’s a handy Gulp plugin called Gulp-Buster that can help bust the cache for you. Add this into your gulpfile.js and configure a task to run it.

I won’t be going into Gulp in this post but if you aren’t familiar with it or need a refresher check out Make JavaScript Easier to Swallow with Gulp. I will however, be going into how to take the Gulp-Buster output and use it in your Kentico website. 

Step 1

After your gulp process runs you will end up with a JSON file. Our file is called busters.json and looks like the following:
 
{"_framework/dist/js/bundle.js":"2592295f2f31be0a77e15abd578aadb4"}


Each time any resource configured for gulp-busting changes, and your Gulp process runs, you’ll get a new hash associated with your file. This file could contain multiple resources and hashes. 

Step 2

Next, we’ll create a few simple functions to parse this file so your website will know that your resource has changed and the cache needs to be cleared. Create a new C# class file, called Fingerprint.cs, that contains the following functions:
 
public static class Fingerprint
{
        /// Reads the cache buster file and caches it so successive hits are fast
        private static Dictionary<string, string> readCacheBuster()
        {
            string busterCache = "busters.json";
            //see if the item is cached
            if (HttpRuntime.Cache[busterCache] == null)
            {               
                //serialize json to dictionary so we can lookup the item we want to bust
                string jsonFile = HostingEnvironment.MapPath("~/busters.json");
                string json = string.Empty;
                string hash = string.Empty;
                using (StreamReader r = new StreamReader(jsonFile))
                {
                    json = r.ReadToEnd();
                    HttpRuntime.Cache.Insert(busterCache, JsonConvert.DeserializeObject<Dictionary<string, string>>(json), new CacheDependency(jsonFile));
                }                
            }

            return HttpRuntime.Cache[busterCache] as Dictionary<string, string>;
        }
        /// Tags the specified root relative path and builds a cache busted compatible URL      
        /// based on https://github.com/madskristensen/vswebessentials.com/blob/master/Website/App_Code/Fingerprint.cs
        public static string Tag(string rootRelativePath)
        {
            //see if the item is cached
            if (HttpRuntime.Cache[rootRelativePath] == null)
            {
                // for debugging allow turning cache busting off
                string absolute = HostingEnvironment.MapPath("~" + rootRelativePath);     
                int index = rootRelativePath.LastIndexOf('/');
                string hash = readCacheBuster()[rootRelativePath.TrimStart('/')];
                string result = rootRelativePath.Insert(index, "/v-" + hash);
                HttpRuntime.Cache.Insert(rootRelativePath, result, new CacheDependency(absolute));               

            }

            return HttpRuntime.Cache[rootRelativePath] as string;
        }
}


Notice we’re leveraging Kentico’s caching mechanism to make sure this process is as streamlined as possible and successive calls to the file will be speedy.

Step 3

We will now make a small tweak to the web.config file that will leverage URL rewriting to build a cache buster compatible URL. Add the following rewrite rule:
 
<rewrite>
       <rules>
           <rule name = "fingerprint" >
                <match url="([\S]+)(/v-[0-9a-z]+/)([\S]+)" />
                <action type = "Rewrite" url="{R:1}/{R:3}" />
           </rule>
        </rules>
</rewrite>

Step 4

Next, we need to make a Kentico macro to call the fingerprinting functionality and serve up the static resource that we want to cache bust. Add the following custom macro to your Kentico website:
 
[MacroMethod(typeof(string), "Cache-buster", 1)]
    [MacroMethodParam(1, "param2", typeof(string), "Relative path to the file you expect to get cache busted")]
    public static object Fingerprint(EvaluationContext context, params object[] parameters)
    {        
        // Branches according to the number of the method's parameters
        switch (parameters.Length)
        {
            case 1:
                // Overload with one parameter                        
                return Fingerprint.Tag(ValidationHelper.GetString(parameters[0], ""));

            default:
                // No other overloads are supported
                throw new NotSupportedException();
        }
    }

Step 5

Finally, we need to serve up the resources by adding the proper call to the page that needs the resource. Go to the page where you reference your resource and replace it with a call that looks like the following:
 
<script src='{ % Fingerprint("/_framework/dist/js/bundle.js") #%}'></script>


Go and hit your page and then view the source. You’ll note your resource inclusion looks like the following: 
 
<script src="/_framework/dist/js/v-2592295f2f31be0a77e15abd578aadb4/bundle.js"></script>

 

You can see that our cache-buster converted our file to the hash equivalent. When the hash changes, you will get a truly new URL, so the site will pull the latest file. Now you know that when you change your client-side resources, your users will get the latest file without any hassle of manual cache clearing.
 

Share This Post:

Twitter Pinterest Facebook Google+
Click here to read more Kentico posts
Start a Project with Us
Photo of the author, Sterling Heibeck

About the author

Sterling Heibeck has been slinging code since 1995. His role at BizStream isn't only about raising the median age of the group, but he brings a multitude of talent to the table when it comes to architecture, design, development, and all around awesomeness. To Sterling, development is 95% problem solving and 5% Google. His answer is always "yes" when asked the question "Can we do [insert anything here]?" He's also proud dad of Kaiyah, Bennet and Morgan.

View other posts by Sterling

Subscribe to Updates

Stay up to date on what BizStream is doing and keep in the loop on the latest with Kentico.