Config-free IHttpModule Registration

Simplifying framework components that plug into the ASP.NET HTTP pipeline by registering HttpModules programmatically without any configuration using a new, hidden ASP.NET 4 feature.

HTTP Request Processing PipelineThe ASP.NET pipeline allows HTTP modules to plug in into the request processing lifecycle and do work at various stages. For example, output caching, authentication, authorization etc. are all implemented as HTTP modules.

However one of the problems is that HTTP modules must be registered in configuration. This is a little painful for writing framework components where an HTTP module is essentially an implementation detail. You don't want every app developer using your framework to have to add in some configuration entries. What you want is the ability to programmatically add HTTP modules to the pipeline. This isn't available out-of-the-box today.

I faced this problem in my previous post around RIA Services, Authentication and Roles, as I needed to handle the PostAuthenticateRequest event. This seems to have come up before (for example here and here on stack overflow).

So in the interim, we can use the new, and somewhat obscure, ASP.NET 4.0 feature, the PreApplicationStartMethodAttribute, that lets you declare some code you want to run early in the initialization phase of your web application, even before any dynamic compilation happens and before any application startup code runs. Combine that capability with an HttpApplication that supports registering HTTP modules programmatically, as we're in business. I wrote DynamicHttpApplication that provides this API (link to code below):

public abstract class DynamicHttpApplication : HttpApplication {
    public static void RegisterModule(Func<HttpApplication, IHttpModule> moduleFactory);
}

You'll need to use this class as your base class in Global.asax.cs in your web app instead of the default which is HttpApplication.

With this in place, I can add a class library as a reference, or place it in the bin directory and programmatically register HTTP modules without requiring additional configuration. For example, say I have a user tracking HTTP module that does something interesting with the authenticated user. Here's the code I can write:

[assembly: PreApplicationStartMethod(typeof(UserTrackerModule), "Register")]

namespace DynamicWebApp.Sample {

    public sealed class UserTrackerModule : IHttpModule {

        #region Implementation of IHttpModule
        void IHttpModule.Dispose() {
        }

        void IHttpModule.Init(HttpApplication application) {
            application.PostAuthenticateRequest += delegate(object sender, EventArgs e) {
                IPrincipal user = application.Context.User;

                if (user.Identity.IsAuthenticated) {
                    DateTime activityDate = DateTime.UtcNow;

                    // TODO: Use user.Identity and activityDate to do
                    //       some interesting tracking
                }
            };
        }
        #endregion

        public static void Register() {
            DynamicHttpApplication.RegisterModule(delegate(HttpApplication app) {
                return new UserTrackerModule();
            });
        }
    }
}

Basically it is a normal/typical IHttpModule implementation that you might write with one thing extra: a static Register method that is declared to be the PreApplicationStartMethod in the assembly's metadata.

HTTP modules provide powerful ways to do interesting things in ASP.NET. For example, we do some pretty creative things in RIA Services with our HTTP module. The ASP.NET infrastructure lets framework components do their job, and now they can do so without needing ugly configuration entries.


Links

Source code containing the DynamicHttpApplication (its fairly simple ... implemented in around ~50 lines of code) that you can use in your own app, along with the sample above.


[ Tags: | ]
Posted on Thursday, 7/1/2010 @ 7:29 AM | #ASP.NET


Comments

9 comments have been posted.

Rahul Soni

Posted on 7/1/2010 @ 9:25 AM
That was really smart. It is so funny, if somebody would have asked about this question from me yesterday, I would have simply said "I don't think it could be done!"

Thank you so much for sharing this Nikhil!!!!

Mahesh Velaga

Posted on 7/2/2010 @ 3:02 AM
Nice Snippet and a great idea !

As a side note, I would use ".Any()" instead of ".Count == 0" for checking if there are any elements in that IEnumerable.

Nikhil Kothari

Posted on 7/2/2010 @ 4:32 PM
@Mahesh

I had a List in the code as opposed to an IEnumerable, and so Count is a more direct way than Any() which I believe checks for a List as well and resorts to Count in that case...

Ruben Prins

Posted on 7/7/2010 @ 4:22 AM
Nice; however, something tells me this is rather fragile. For instance, the recently released Razor engine also requires its own HttpApplication-derived Global.asax (for hooking up its web page routing support, so details.cshtml can be accessed though details/123, and registering its build providers). In fact, it even registers its HttpApplication-class in the PreApplicationStartMethod by setting PageParser.DefaultApplicationBaseType. Which even makes it a no-code solution, too! (Don't you just love Reflector?) Which I guess means it cannot work together with this solution.

Wouldn't it be better if there were a couple of static events within the core of ASP.NET like HttpApplication.BeforeInit/AfterInit? That way each PreApplicationStartMethod could hook up cleanly, which does not seem possible this way. Being able to register IHttpModules and IHttpHandlers programmatically would also solve this, I guess.

Nikhil Kothari

Posted on 7/8/2010 @ 1:31 AM
@Ruben
The sample represents what you can do today, based on limitations of the core platform.

Yes, of course things can be done to add this capability in the core platform, and that will happen undue course of time. Meanwhile, the problem/scenario exists and some solution is interesting. Also in the meanwhile, multiple solutions to the same problem like the Razor one probably can't coexist, since they're both trying to do the same thing! :)

Miguel Fernandes

Posted on 7/15/2010 @ 6:45 AM
This is an interesting approach!

How can I get the client call to Autentication.LoadUser to work, so it can remember the last logged in user?
My guess is the FormsAuthenticationService implementation of GetUser should look at the _ASPXAUTH cookie and return the deserialized user, or is there a better way to do it?

Nikhil Kothari

Posted on 7/20/2010 @ 10:22 PM
@Miguel
When you call Authentication.GetUser it looks at the currently authenticated principal, which will cause FormsAuthenticationService to deserialize a User object from the forms auth ticket stored in the forms auth cookie.

Tyrone Davis

Posted on 7/25/2010 @ 4:17 PM
I'm you use a lamba expression to shorten the registration code to this?

public static void Register() {
DynamicHttpApplication.RegisterModule((app) => return new UserTrackerModule());
}

However, this was a nice tip indeed. Thanks

Miguel Fernandes

Posted on 7/27/2010 @ 3:47 AM
Turned out to be a cookie persistence problem, the expiration time was not being passed along.
Solved it by setting the cookie expiration time in FormsAuthenticationService.Login like this:

authCookie.Expires = ticket.Expiration;
Post your comment and continue the discussion.