Monday, March 16, 2015

CDN fails, but your scripts don’t have to – fallback to local jquery

Bundling with Asp.Net MVC


If you are working on Asp.Net either Web Forms or MVC, you would have come across Bundling & Minification of JavaScript & Css files.

Background:-

There are hardly any applications which don’t use JavaScript or Css files and as our application grows, we tend to split these files logically into multiple files. We then include each file individually on the HTML page which is sent to the browser. The browser then resolves the dependencies on these files by making the request to the server for each file which can slow down the loading of your page.

In .Net 4.5, a powerful tool was launched which allows you to bundle multiple files into one file, which then downloaded by the browser in one single request to the server. Below how it works:-

A typical example of defining a bundle in BundleConfig.cs is given below:-

  bundles.Add(new ScriptBundle("~/bundles/jqueryval").Include(
                        "~/Scripts/jquery.unobtrusive*",
                        "~/Scripts/jquery.validate*",
                        "~/Scripts/requiredif-validation.js"));


The code above just creates a new instance of ScriptBundle and assigns it the virtual path as "~/bundles/jqueryval". It then invokes the include method to specify which files should be included in the bundle.

Now that the bundle is registered, we can reference it in the views using below statement:-

@Scripts.Render("~/bundles/jqueryval")

Now, before you run this, you should know that, by default, in debug mode bundling is switched off to enable developers debug the files if they want to. Although you can override this behavior either from web.config by changing the value of debug to false as shown below:-

<compilation debug="false" targetFramework="4.5" />

Or override these settings by writing below line in BundleConfig.cs:-

BundleTable.EnableOptimizations = true;

Once you do that, next time you run the application, you should see something like below as the file included on your page on browser:-

<script src="https://yoursitename.com/bundles/jqueryval?v=JzhfglzUfmVF2qo-weTo-kvXJ9AJvIRBLmu11PgpbVY1"></script>

Using CDN with bundling:-

In above example, we created a bundle which loads all files from our server but what if you wish to load files from CDN. Files like JQuery are so commonly used that they are available on almost all CDNs & loading these files from CDN can noticeably improve the load time of your web page.

Let’s see how it can be implemented.

First, the declaration of bundle for a CDN changes as shown below:-

var jqueryBundle = new ScriptBundle("~/bundles/jquery", @"https://ajax.ggleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js").Include(
                "~/Scripts/jquery-{version}.js");
bundles.Add(jqueryBundle);

Above we did two things; first we defined the CDN location of the file and then the local version of the same file. In debug mode, the local version will be used and in Release mode, the CDN version will be picked.

The second step is to enable the CDN usage in RegisterBundles method by adding below line:-

bundles.UseCdn = true;

Once this is done, the CDN version will be used in Release mode and the local version will be used in debug mode which obviously as I stated earlier, you can override it.

Now this is all good but whenever you use some third party things in your application, you always have to plan for the failures i.e. in this case let’s say either CDN is down or the file is no longer hosted there. You can't let this break your web page and hence you need a fallback option. Luckily, this is available with bundling in Asp.Net MVC.

Bundling with fallback:-
You can do few extra things in above CDN bundling code to enable fallback which then pick these files from your server in case of CDN failure.

First, add the CdnFallbackExpression in the bundle as mentioned below:-

  var jqueryBundle = new ScriptBundle("~/bundles/jquery", @"https://ajax.ggleapis.com/ajax/libs/jquery/1.10.2/jquery.min.js").Include(
                "~/Scripts/jquery-{version}.js");
jqueryBundle.CdnFallbackExpression = "window.jquery";
bundles.Add(jqueryBundle);

Here you just define a JavaScript method which will be called if it fails to load the file from CDN. You then need to implement this JavaScript method and load the file from your own server as shown below:-

<script>(window.jQuery)||document.write('<script src="/bundles/jquery"><\/script>');</script>

The basic idea for CDN fallback is to check for a type or variable that should be present after a script load, and if it's not there, try getting that script locally.

Once you do this, you are ready to use the bundling with CDN with successful fallback applied and you can be 100% sure that you web page will get the files it needs from one source or another.

1 comment :