Skip navigation.
An HttpHandler is a class that implements the IHttpHandler interface. This class can be associated with pretty much any file extension. Once you request a resource with a certain extension a corresponding HttpHandler processes your request and sends back a response. For example, when you request an ASP.NET page with the .aspx extension the System.Web.UI.Page handler processes the request and responds with an HTML page.
IHttpHandler
aspx
System.Web.UI.Page
We're going to write a custom handler and associate it with .css files. This handler will allow us to add variables to our style sheets. For example, we'll define variables and their values in a configuration file (how about web.config?):
css
web.config
<DynamicCss> <variables> <variable name="$main_color" substitute="#fff" /> <variable name="$bkg_color" substitute="gray" /> </variables> </DynamicCss>
and then use the variables in a style sheet:
body { background: $bkg_color; color: $main_color; }
When a .css file is requested our HttpHandler will replace the variables on the fly. To make this happen we'll need to implement two pieces:
HttpHandler
You may extend the standard set of settings in web.config with your own settings. This is great news because you may add any configuration settings you want as long as you also write a handler to digest these settings. A configuration section handler is a class which implements the IConfigurationSectionHandler interface:
IConfigurationSectionHandler
using System; using System.Configuration; using System.Xml; using System.Xml.Serialization; namespace DynamicCSS { public class SettingsHandler : IConfigurationSectionHandler { private XmlSerializer _ser = new XmlSerializer (typeof (Settings)); public object Create (object parent, object configContext, System.Xml.XmlNode section) { Settings cfg = (Settings) _ser.Deserialize (new XmlNodeReader (section)); return cfg; } } }
To put this handler to good use you need to register it in web.config:
<configSections> <section name="DynamicCss" type="DynamicCSS.SettingsHandler, DynamicCSS" /> </configSections>
The Settings class in a listing above is going to handle variable names and their values:
Settings
using System; using System.Collections; using System.Xml; using System.Xml.Serialization; namespace DynamicCSS { [XmlRoot("DynamicCss")] public class Settings { private Variable[] _variables; [XmlArray ("variables")] [XmlArrayItem ("variable", typeof (Variable))] public Variable[] Variables { get { return _variables; } set { _variables = value; } } } /// <summary> /// This class represents a variable and its substituted value /// </summary> public class Variable { string _name; string _substitute; public Variable () {} [XmlAttribute ("name")] public string Name { get { return _name; } set { _name = value; } } [XmlAttribute ("substitute")] public string Substitute { get { return _substitute; } set { _substitute = value; } } } }
Variable is a painfully simple class that represents a variable and its value (declared in web.config, as we agreed), while the Settings class is another painfully simple class which simply implements a collection of these variables in an array. You might've noticed the use of an XML serializer and XML attributes in both Settings and Variable. XML serialization is outside the scope of this article, but suffice it to say you only need to carefully match serialization attributes with XML elements and attributes in your configuration section. The XML serializer will take care of the rest! This is both elegant and powerful!
Variable
Now that we've defined variables, their values, put them in web.config and wrote a handler to read them, we'll implement the final and most important piece: an HttpHandler itself. Please, download the source code of the handler and poke around. I will only outline the steps our handler is supposed to take:
.css
Since we've discussed a configuration section handler in great detail here's how you iterate over variables in the HttpHandler:
Settings cfg = (Settings) System.Configuration.« ConfigurationSettings.GetConfig ("DynamicCss"); foreach (Variable var in cfg.Variables) { // ------------------------------------ // Search for the variable // NOTE: The search is case-sensitive // ------------------------------------ Regex re = new Regex (string.Concat ( "\\$\\b", var.Name.Substring (1), "\\b")); css = re.Replace (css, var.Substitute); }
I use a simple regular expression to replace variable occurrences.
NOTE: Variable names are case sensitive. If you want to "relax" this condition you need to add RegexOptions.IgnoreCase to the Regex constuctor.
RegexOptions.IgnoreCase
Regex
At this point we've got all bits and pieces in place. One last thing is to configure IIS to cooperate with us and we're truly done.
When you install the .NET Framework a number of file extensions are associated with the ASP.NET ISAPI. For example, IIS hands requests for .aspx, .asmx and .ashx files to aspnet_isapi.dll. On the other hand, .css files are not mapped to .NET and therefore IIS handles them on its own, and our HttpHandler won't be even invoked. We need to remap .css files to the ASP.NET ISAPI so the handler will get a piece of the action.
.aspx
.asmx
.ashx
aspnet_isapi.dll
To do so launch the Internet Information Manager applet from Administration Tools, find your web app, righ click and go to Properties. On the Virtual Directory tab click Configuration. On the Mappings tab click Add and configure extension mapping as shown below:
The ISAPI path is quite long, so simply copy and paste it from another extension. Done and done.
A good fit for this technique is branding web applications. For example, you have to configure color schemes of your web app for different instances. We do it with our main product. Making a separate style sheet for each instance is painful enough as the customer base grows. Introducing variables makes matters much easier since you don't have to tweak CSS once you make it variable-oriented.
Using this technique would be wrong if you plan on changing variables often. Remember we store them in web.config? Each time you edit web.config you cause your web app to restart. Often restarts will degrade performance among other things, as I explained in my article Beware Of Deploying Debug Code In Production. Therefore you might want to configure variables and leave them alone for a while.
If you do need to change variables often I'd recommend you move them out of web.config into a separate XML file, for example. In fact, you won't need a configuration section handler any more. All you need to do is to somehow feed variables to the HTTP handler.
Scott Watermasysk kindly provides source code of his blogging engine, .Text. If you feel adventurous poke around it—you'll find many interesting code snippets in there. I borrowed the idea of sniffing for the If-Modified-Since header from Scott.
If-Modified-Since
Liked it? Hated it? Discuss this article
My designer and developer OPML feed subscriptions are available for grabs.