McFunley: did you name this page something impossible to remember correctly on purpose?
McFunley: this is a good anti-querystring hacking technique
DarrelHerbst: the twocheckboxcolumn.aspx?
DarrelHerbst: i remember it
DarrelHerbst: your mind is addled with coffee
McFunley: i typed “twocolumncheckbox” and “twocheckcolumnbox” and “boxcolumnchecktwo” before I got it right
DarrelHerbst: addled
Brad Abrams had a recent post about how the usual way of raising an event in C#:
protected virtual void OnMyEvent(EventArgs e)
{
if(MyEvent != null)
{
MyEvent(this, e);
}
}
is not threadsafe. Purely out of curiosity, I wondered if VB handles this the correct way:
protected virtual void OnMyEvent(EventArgs e)
{
EventHandler handler = MyEvent;
if(handler != null)
{
handler(this, e);
}
}
Since VB abstracts this away with the RaiseEvent keyword. Unfortunately it doesn’t seem to, as this code:
Protected Overridable Sub OnMyEvent(ByVal e As EventArgs)
RaiseEvent MyEvent(Me, e)
End Sub
Compiles to this:
.method family newslot virtual instance void OnMyEvent([mscorlib]System.EventArgs e) cil managed
{
// Code Size: 22 byte(s)
.maxstack 8
L_0000: ldarg.0
L_0001: ldfld [mscorlib]System.EventHandler VbConsoleApp.Test::MyEventEvent
L_0006: brfalse.s L_0015
L_0008: ldarg.0
L_0009: ldfld [mscorlib]System.EventHandler VbConsoleApp.Test::MyEventEvent
L_000e: ldarg.0
L_000f: ldarg.1
L_0010: callvirt instance void [mscorlib]System.EventHandler::Invoke(object, [mscorlib]System.EventArgs)
L_0015: ret
}
Which has a race condition—it should store the delegate in a local. Generally, the answer to these questions is “it is fixed in 2005” — so I’m going to just assume that that is the case here.
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>