HttpModule for Performing a Custom Action for Certain URLs
January 13th, 2005
I wrote this collection of classes over the holidays as part of a side project and forgot about it. Today at work I needed basically exactly the same thing, but unfortunately I didn’t have it handy and had to recreate it from scratch.
This is a way to execute any action you please for requests against url’s matching one or more regular expressions. Today at work, I used it to install a response filter for a specific set of .aspx pages. I can think of at least a few more reasons someone might want something like this, so I’m posting it.
using System; using System.Text.RegularExpressions; using System.Configuration; using System.Collections; using System.Web; namespace McKinley.Web { /// <summary> /// An abstract <see cref="T:System.Web.IHttpModule"/> that checks /// the url of each incoming request against one or more regular /// expressions. When a match is found, a custom action is /// performed. /// </summary> public abstract class BaseRegexModule : IHttpModule { private RegexChain _chain; /// <summary> /// Reads the list of regular expressions to match from /// the configuration settings. Hooks the beginning of /// each request to perform the checks. /// </summary> public void Init(HttpApplication context) { Hashtable settings = (Hashtable)ConfigurationSettings.GetConfig(this.ConfigSectionName); if(settings != null) { _chain = new RegexChain(); foreach(object val in settings.Keys) { string pattern = val as string; if(pattern != null && pattern.Length > 0) { _chain.Add(pattern); } else { throw new ConfigurationException( "The regular expression pattern may not be empty."); } } } context.BeginRequest += new EventHandler(BeginRequest); } /// <summary> /// Tries to match the raw url against the list of expressions /// given to the module. If a match is found, calls the abstract /// <see cref="M:CustomAction"/> method. /// </summary> private void BeginRequest(object sender, EventArgs e) { HttpApplication application = sender as HttpApplication; if(application != null) { HttpRequest request = application.Request; if(request != null && _chain.IsMatch(request.RawUrl)) { CustomAction(application); } } } /// <summary> /// This must be overridden to provide the name of the configuration /// section for the concrete module class. The section is presumed /// to use a <see cref="T:System.Configuration.DictionarySectionHandler"/>. /// </summary> protected abstract string ConfigSectionName { get; } /// <summary> /// This must be overridden to define a custom action to be performed /// when one of the regular expressions are matched against the /// request's URL. /// </summary> protected abstract void CustomAction(HttpApplication context); public void Dispose() { } public BaseRegexModule() { } } /// <summary> /// This is an efficient utility class for a list of /// regular expressions evaluated together. /// </summary> internal class RegexChain { private class Node { public Regex Expression; public Node Next; public Node(Regex r) { Expression = r; } } private Node _head; private Node _tail; /// <summary> /// Returns true if the given string is a match /// for any of the regular expressions in the chain. /// </summary> public bool IsMatch(string input) { Node n = _head; while(n != null) { if(n.Expression.IsMatch(input)) { return true; } n = n.Next; } return false; } /// <summary> /// Adds the given pattern to the chain of regular /// expressions. /// </summary> public void Add(string pattern) { Node n = new Node(new Regex(pattern)); if(_head == null) { _head = n; } else { _tail.Next = n; } _tail = n; } } /// <summary> /// This is a <see cref="T:System.Configuration.IConfigurationSectionHandler"/> /// that can be used with a class derived from /// <see cref="T:McKinley.Web.BaseRegexModule"/>. /// </summary> public class RegexModuleSectionHandler : DictionarySectionHandler { protected override string KeyAttributeName { get { return "pattern"; } } } }
And there you have it. Here’s an extremely simple example class that uses this to set a cookie for matching url’s.
public class ExampleRegexModule : BaseRegexModule { public ExampleRegexModule() { } protected override void CustomAction(System.Web.HttpApplication context) { context.Response.AppendCookie(new HttpCookie("Oscar", "Is god")); } protected override string ConfigSectionName { get { return "mckinley.web/exampleRegexModule"; } } }
And finally, here are the web.config entries you’d need to install this HttpModule.
<configSections> <sectionGroup name="mckinley.web"> <section name="exampleRegexModule" type="McKinley.Web.RegexModuleSectionHandler, McKinley.Web" /> </sectionGroup> </configSections> <mckinley.web> <exampleRegexModule> <add pattern="somepage.aspx" /> </exampleRegexModule> </mckinley.web> <system.web> <httpModules> <add name="example" type="McKinley.Web.ExampleRegexModule, McKinley.Web" /> </httpModules> <system.web>