Sunday, February 13, 2011

Register your HTTP modules at runtime without config

In ASP.NET 4, we added the concept of a PreApplicationStart method that an assembly can use to execute code early on in the appdomain without any configuration. Phil Haack covered it a while back in this post. It’s pretty simple to use. You just define a class that looks like:

public class PreApplicationStartCode {
public static void Start() {
    // Your startup code here
}
}

And then you add an assembly level attribute pointing to it:

[assembly: PreApplicationStartMethod(typeof(PreApplicationStartCode), "Start")]

With the release of MVC3 and ASP.NET Web Pages, we added another little gem: a RegisterModule() API that lets you dynamically register an IHttpModule without touching config. Sadly, the method is hidden so deep that it is hard to find by accident (it’ll get cleaned up in the next framework version).

By combining the two techniques, you have everything you need to register a module dynamically, e.g.

public class PreApplicationStartCode {
public static void Start() {
    // Register our module
    Microsoft.Web.Infrastructure.DynamicModuleHelper.DynamicModuleUtility.RegisterModule(typeof(MyModule));
}
}

I warned you it was well hidden! :)

The module type that you pass in to that is just a standard IHttpModule, e.g. here is a basic module that writes to the response on every request:

class MyModule : IHttpModule {
public void Init(HttpApplication context) {
    context.BeginRequest += (sender, e) => {
        var response = ((HttpApplication)sender).Response;
        response.Write("MyModule.BeginRequest");
    };
}

public void Dispose() { }
}

The beauty of this is that it allows you to create fully encapsulated assemblies that you can just drop into a web app’s bin folder and have them light up without having to add any ugly registration to the app.

And yes, all this works fine in partial trust!

You can download a minimal sample from here.

25 comments:

  1. you're right, it's super deep..:D

    without your post i'd have no idea that it's exists. thanks for sharing.

    ReplyDelete
  2. Excellent stuff... but what about HttpHandlers??
    Can they be auto-registered too??

    ReplyDelete
  3. Great minds must think alike - I posted on this not quite a week ago. :) http://www.paraesthesia.com/archive/2011/02/08/dynamic-httpmodule-registration-in-asp-net-4-0.aspx

    ReplyDelete
  4. @James_2JS: sorry, I don't know of a way to do this for HttpHandlers, though I agree it would be useful

    ReplyDelete
  5. @Travis: wow, very similar post indeed! :)

    ReplyDelete
  6. I think this is not specific to ASP.NET MVC or WebPages. Every one can just add a reference to Microsoft.Web.Infrastructure.dll in his class library and use it in ASP.NET Web Forms, ASP.NET MVC or ASP.NET Web Pages.

    ReplyDelete
  7. Nikhil had posted something similar also... http://www.nikhilk.net/Config-Free-HttpModule-Registration.aspx

    ReplyDelete
  8. David
    Please post a sample project with 2 dll:
    One that exists by default in the MVC project
    One that does not exists in the MVC project and , when dropped , is instantiated( put something in the Application...)

    Thank you
    Andrei

    ReplyDelete
  9. @Andrei: are you running into a specific issue with getting this to work?

    ReplyDelete
  10. Question, using this method - is there anyway to programatically setup the equalivent entries needed in web.config? Or, do you still need those? You mentioned that you'd just need to drop the dll in the bin, but that's the only missing peice (at least in my scenario) to get that to work .. any ideas?

    Thanks, @djbyter

    ReplyDelete
  11. @korifrancis: the main appeal of this technique is in fact that you don't need to modify web.config at all! :)

    ReplyDelete
  12. No specific issue - just, being a lazy programmer, I want a simple test first and then to understand how. Please , please help me( and other lazy programmers)

    ReplyDelete
  13. Ok, by popular demand, I included a minimal app. Link is at the end of the post. It shows the feature being used in the main assembly of an MVC app, but it's *identical* in any other assembly.

    ReplyDelete
  14. This is very helpful, thank you! Btw it's 2 years later and I still see DynamicModuleUtility hidden in the same place! :-P

    ReplyDelete
  15. @rushonerok: actually, in Framework 4.5 there is a much cleaner place to find this method: HttpApplication.RegisterModule. But the 'hidden' one still exists for backward compat.

    ReplyDelete
  16. It's good but it can handle only managed requests. What if I want to handle *.js requests?

    ReplyDelete
  17. @Halil I think can do that if you turn on runAllManagedModulesForAllRequests (search web for details on using this).

    ReplyDelete
  18. @David, it's ok but I don't want to do it since;

    1) I don't want to modify web.config, if I do, I can register module it web.config. Wyh I try to register it dynamically by code.
    2) It has a little performance problem.

    ReplyDelete
  19. @Halil: I don't know of any other solution. Without that flag, requests to static files don't even go to ASP.NET at all, so none of what is described in this post applies. Note that it's not specific to this technique, but you'd see the same thing if you manually registered your module in web.config.

    ReplyDelete
  20. This comment has been removed by the author.

    ReplyDelete
  21. @David,

    If I register in web.config, it handles all request. In web.config/system.webServer/modules

    <add type="Taskever.Web.App_Start.MyModule" name="sasdasd">

    It works since I did not specified preCondition.

    ReplyDelete
  22. @David, I thank you so much for your quick response.

    ReplyDelete
  23. Maybe you're right. Unfortunately, I really don't see how the technique described here can work without RAMMFAR since the requests just don't make it there. And I agree it would be useful; I just don't know how to make it work.

    ReplyDelete
  24. @Thanks again. I'm just trying to develop a framework that is used to be a start point for asp.net mvc applications (https://github.com/hikalkan/aspnetboilerplate). It has a feature to create web api classes dynamically over your application service layer using castle dynamic proxy. And also creates javascript proxies to need to simplify web api calls. So, I need to handle .js calls for a specific route and create file dynamically using reflection.

    Thank you very much for your responses.

    ReplyDelete