--- title: Zero-configuration ASP.NET modules and handlers layout: post ---
A couple of my open source projects, Cassette and Not Found MVC, add custom HTTP handlers and modules to the host application. Cassette, for example, can return bundled assets from a route added to the ASP.NET RouteTable. It does this without requiring any additions to Web.config or Application_Start in Global.asax.
Removing the requirement of editing configuration or adding initialization code makes it simpler to use a library. A developer can just reference the library and they're ready to go.
Achieving this simplicity requires two handy libraries, called WebActivator and Microsoft.Web.Infrastructure. Both are available via Nuget.
Using WebActivator's PreApplicationStartMethod
attribute we can provide a method to run very early
in the web application's start up process. The method can use DynamicModuleUtility
,
from the Microsoft.Web.Infrastructure
assembly, to register HTTP modules.
[assembly: WebActivator.PreApplicationStartMethod( typeof(Example.StartUp), "PreApplicationStart")]
namespace Example
{
public static class StartUp
{
public static void PreApplicationStart()
{
DynamicModuleUtility.RegisterModule(typeof(MyHttpModule));
}
}
}
HTTP modules are useful for adding functionality that applies to all requests. For example, Cassette uses an HTTP module to rewrite HTML output of pages. However, if you want to handle your own custom URLs you need to create an HTTP handler hooked up via the ASP.NET routing system.
Adding custom routes needs to happen after the application itself has finished starting up.
It's too early to add routes in the PreApplicationStartMethod
.
Instead, use the PostApplicationStartMethod
attribute also provided by WebActivator.
The method specified in PostApplicationStartMethod
is run after the web application's Application_Start method.
It's the ideal place to modify the route table.
This is the attribute:
[assembly: WebActivator.PostApplicationStartMethod( typeof(Example.StartUp), "PostApplicationStart")]
And here's the method added to the StartUp
class:
public static void PostApplicationStart()
{
var route = new Route("my/url", new MyRouteHandler());
// Insert route at front of route list to avoid conflicts with application routes.
RouteTable.Routes.Insert(0, route);
}
The application will have already defined its own routes, so we need to ensure our library's routes don't conflict.
For example, the application's "{controller}/{action}"
route would capture something like "my/url"
.
So if our library needs to handle that specific URL, then its route must be first in the route table.
Another precaution you can take is to constrain your route to only match incoming requests. The routing table is also used to generate URLs, so you may need to guard against unintended matches. Here's an example constraint:
class OnlyMatchIncomingRequestsConstraint : IRouteConstraint
{
public bool Match(HttpContextBase httpContext, Route route, string parameterName, RouteValueDictionary values, RouteDirection routeDirection)
{
return routeDirection == RouteDirection.IncomingRequest;
}
}
Use it like this:
var route = new Route(
"my/url",
new RouteValueDictionary(),
new RouteValueDictionary(new { incoming = new OnlyMatchIncomingRequestsConstraint() }),
new MyRouteHandler()
);
See these techniques in action in the source code of Cassette and NotFoundMVC over on GitHub:
Have you seen any other cool uses of WebActivator out there? Let me know in the comments!