Nancy on OWIN – bundling and minification

Continuing with changeset from last time.

I want to introduce bundling and minification without dependency on System.Web (it would defeat purpose of trying to create site on OWIN).

For that, I’m using SquishIt:

Install-Package SquishIt

I found some resources how to use it in Asp.Net Mvc and there is also integration manual on Nancy documentation, none of them is covering case when Nancy is running outside of IIS (and without Razor engine, which I don’t believe it is necessary for this simple example, I might want to add it if I see that I need it, but I want to try to go without it), but they are good samples to see how to use SquishIt API. For bundling, I need bootstraper, which is equivalent of Global.asax.cs in Asp.Net application, and as I want to do minimal customizations, I will use default bootstrapper as base for mine:

 

I’m taking care of bundling itself in separate class, BundleConfig, as Bootstapper is place where all of initialization is performed, and if all that stuff is inside it will become very dirty and cluttered in notime:

Bundle config

I’m separating JavaScript and Css bundling, and within JavaScript, creating one bundle for all libs (and only angular is in it), and another for my “app” where I put whatever js is in app folder.

To keep things simple, and continue using html view, which can only replace tokens from passed view model, I’m going to reference these bundles and pass them to view from my HomeModule:

Bundles added to viewmodel

That produces already complete html tags, so that I can only write them directly in view:

Result:

As a side note, take a good look on contents of this file. You can see that WelcomeCtrl was minified, and minifier used (MsMinifier) did shortening of controller dependencies (which was just ‘$scope’ in this case). Take care when writing applications in Angular to explicitly identify dependencies (by defining modules in array, with dependency names as strings first, then your module as last element of array, like here, “$scope” is a string, and minification does not change it). That allows Angular’s injector service to identify your dependencies and inject them even when minifier performs obfuscation of argument names to reduce amount of characters used.

SquishIt works by taking all of files added to bundle, and packing them together into another file,  that will have given name. If you want to leverage browser caching, then you need to add “#” to file name, and it will be replaced with a “token”, that looked to me as hash at first, but I figured that it must be just a random guid, as it did recreate scripts combined js file without changing any of contents, so it just creates a file with random guid in name and remembers it under given name for lifecycle of application instance.

That is very simple approach, as then all that need to be done is render include tag that references that file name as source, and when something is changed, new file name and url will be created, so no outdated files will be used, and no browser cache will cause problems. It seems very simple and effective, but don’t do it like this! This is just a step to learn how it works, and while it seems fine, it is not the simplest thing that could possibly work. It will create new file every time application is restarted, and if you are hosting your application, you may not be able to control app pool life cycle, which may have rather conservative life policy. If your application works good and you don’t have to check on it, it may generate hundreds of hundreds of these minified files. Another, even more dangerous problem is that I placed my minified scripts file into same directory with other files, while at same time including whole that directory into bundle! That means every time when app pool is recycled, new bundle will be created with all files, and previous bundle(s), effectively doubling size of app folder on every start of application. This exponential progression can go undetected while doing test run of application, because angular tends to be pretty resilient, and even if you redefined some modules few times, it will work, but after couple dozens of restarts, size of served JavaScript can grow to megabytes, effectively killing browser, and eating your outgoing bandwidth on server. And taking your hard drive space. All of that can be pretty expensive. You understand now why I wrote don’t do it in red, it is very dangerous to use it, even if you put your minified file into another folder, you can forget, or someone else who needs to change something will see how bundles are defined and put it there not knowing how it works, or your hosting provider might decide to migrate your web site to new server and reset permissions in process, so you end up with:

What is another reason to avoid it, is that while compilation debug=”true” is set in web.config, SquishIt works same as standard bundling from Microsoft.Optimization, and will not do any minification, it will serve resources unchanged, as in manual version from start of this post, with only difference being that you don’t have to manually include every file from app folder. That makes developer life much easier, but if you don’t deliver version every day (it is as easy as defining publish profile and configuring server for web publishing, and publish in release mode), it will hide this problem until very late in development life cycle. You may figure out something is wrong if you disable debug and then re-enable when you see minified version being picked up too:

 

So, I hope that by now I convinced every single reader that read to this point, that this approach should not be used for serious application. Ever. This can be avoided by using caching of minified resources in memory. It is faster, doesn’t cause hit to hard drive on every request, it does not require write permissions for application pool account, and does not leave any files behind that must be cleaned up. There is already example in Nancy doc, under Advanced – Diskless Caching.

Based on that example, I created my BundlesModule:

This module is working from /bundles subpath, and also uses GZip to compress outgoing stream, and adds etag and Cache-Control headers to leverage browser caching. In debug mode I intentionally reduced caching to 45 seconds, so even if you create bundle with .ForceRelease to enable minification in debug mode, it won’t be cached for long as you will probably be changing files.

To be able to use dynamic parameters type, I had to add reference to Microsoft.CSharp.

As name of bundle is now passed as part of url ({name} part after /bundles/js/), creation of bundles must be changed accordingly, as they were created with different names:

 

Bundle name is now encoded into url in place where BundleModule expects {name} parameter, so rendered url will point back to that module, and pass bundle name that needs to be served:

Screenshot 2014-04-17-00-28-24-4817544

 

There is no more writing to disk, no need for permissions, and we have bundling solution that is as effective as one from Asp.Net Mvc.

Source code is at github, as usual. From here there are many things that need to be done to make one application, and I still have not decided what to do first. If you have suggestions, please leave a comment. Thanks!

 

 

Leave a Reply