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>