HttpCombiner is a HttpHandler allows you to gather all css and js files together which decreases amount of http requests made to server when user requests a page and also to cache the generated files, which makes you websites to load faster. You can download http handler from Microsoft’s website by clicking here and you can read Omar’s article on how to implement the handler here. Original handler written by Omar Al Zabir.
It took me few minutes to configure the handler to work, it is easy to use and well documented. So i advice you to read Omar’s post before continuing with modifications.
What the problem with Omar’s http handler ?
- needless number of paremeters which are passed to handler, instead of having 1 parameter ie. css or js , we got 3 parameters. Why to have file type and content type together when js extension clearly means “javascript/text” content type.
- generated by handler output is cached which means old version will be used anytime you access the page which makes imposible to use handler during developement process when we constantly modify JS and CSS files. By default there is a parameter in query string call to handler “v” which stands for version but each change you make on files you will need to update the version number so there won’t be caching.
- each JS or CSS file must be defined in web.config which goes against my dynamic thinking
if you find these as faults in usability like i do continue reading further …
How do we fix those issues ?
First of lets get rid of parameters and just pass one that stands for file type (ie. css or js), second of all lets define folder within the application where all js and css files will be stored so we can loop though folder and dynamically get all files at once without defining each new file and last let’s define global parameter which tells handler whether it is production, development server or just a cache refresh to update website so we won’t directly deal with versioning.
I could rewrite the handler to work as i want it to but Omar did a great job with handler and all credit to him, while i don’t want to create “yet another HttpHandler” to combine css/js files.. My idea was to hack HttpCombiner as fast as i could so i could get to work with it without dealing with it’s design.
Implementation
Step I — web.config
let’s create two folders inside of our assets folder css and js, because handler already loads css you need to move all css files from your App_Theme/ThemeName folder to Assets/Css one and Js files just keep inside Assets/Js.
important note, from now on your CSS and JS files are virtually located on the root (where you place the HttpCombiner.ashx) and not inside real Assets/Foldername folder.
Once we have two folders and we understood where files gonna reside and where their virtual location will be, let’s tweak web.config file to work with new settings.
old code
</appSettings> <appSettings> <add key="Set_Css" value="~/App_Themes/Default/Css1.css,~/App_Themes/Default/Css2.css"/> <add key="Set_Javascript" value="~/Javascripts/Js1.js,~/Javascripts/Js2.js,http://ajax.googleapis.com/ajax/libs/jquery/1.2.6/jquery.min.js"/> </appSettings>
new code
<appSettings> <add key="css" value="~/assets/css/"/> <add key="js" value="~/assets/js/"/> <add key="refreshCombiner" value="true"/> </appSettings>
Step II — change of urls layout for handler calls
old urls
<link type="text/css" rel="Stylesheet" href="HttpCombiner.ashx?s=Set_Css&t=text/css&v=1" /> <script type="text/javascript" src="HttpCombiner.ashx?s=Set_Javascript&t=type/javascript&v=2" ></script>
new urls
<link type="text/css" rel="stylesheet" href="HttpCombiner.ashx?t=css" /> <script type="text/javascript" src="HttpCombiner.ashx?t=js" ></script>
Step III — modification of HttpCombiner.ashx
old code — lines 22–24
string setName = request["s"] ?? string.Empty; string contentType = request["t"] ?? string.Empty; string version = request["v"] ?? string.Empty;
new code
string setName = request["t"] ?? string.Empty;
string contentType = (setName == "js") ? "text/javascript" : "text/css";
string version = "1";
if (bool.Parse(ConfigurationManager.AppSettings["refreshCombiner"]))
{
version = new Random().Next(1, 999999).ToString();
}
old code — lines 47–56
string setDefinition =
System.Configuration.ConfigurationManager.AppSettings[setName] ?? "";
string[] fileNames = setDefinition.Split(new char[] { ',' },
StringSplitOptions.RemoveEmptyEntries);
foreach (string fileName in fileNames)
{
byte[] fileBytes = this.GetFileBytes(context, fileName.Trim(), encoding);
writer.Write(fileBytes, 0, fileBytes.Length);
}
new code
string filesPath = ConfigurationManager.AppSettings[setName];
string[] fileEntries = Directory.GetFiles(HttpContext.Current.Server.MapPath(filesPath));
foreach (string fileName in fileEntries)
{
byte[] fileBytes = this.GetFileBytes(context, filesPath + fileName.Trim().Remove(0, fileName.LastIndexOf("\\")), encoding);
writer.Write(fileBytes, 0, fileBytes.Length);
}
Hope you enjoyed it !!!
Tags: http handler, optimization