My original title for this blog post was going to be HttpIoC - Find Out What It Means To Me, as Steven Smith suggested via twitter, but I figured it wasn’t going to be as searchable. :) So I went with the boring title. Sorry Steven!
A while back, Tuna Toksoz and I were having a conversation on twitter about how he does registration of any modules his application will need/use. After playing around with the concept for a little, I was able to get something that will work for our any ASP.NET application with minimal effort.
Problem
Your implementation of IHttpModule within your ASP.NET app has some dependencies that you want to provide via constructor dependency injection.
Solution
The easiest way to do this is to create your own class that inherits from HttpApplication and handle module registration manually. For example, see AutowireApplication below:
1: namespace Common
2: {
3: using System;
4: using System.Collections.Generic;
5: using System.Linq;
6: using System.Web;
7:
8: public class AutowireApplication : HttpApplication
9: {
10: public IServiceLocator ServiceLocator
11: {
12: get { return Application.Get("serviceLocator") as IServiceLocator; }
13: set { Application.Set("serviceLocator", value); }
14: }
15:
16: protected void Application_Start()
17: {
18: ServiceLocator = GetServiceLocator();
19:
20: RegisterComponents(ServiceLocator);
21: }
22:
23: public override void Init()
24: {
25: base.Init();
26:
27: // Register all system components
28: InitializeModules(ServiceLocator);
29: InitializeComponents(ServiceLocator);
30: }
31:
32: protected virtual void InitializeModules(IServiceLocator locator)
33: {
34: List<IHttpModule> modules = locator.ResolveServices<IHttpModule>();
35: if (modules == null) return;
36:
37: foreach (IHttpModule module in modules)
38: {
39: module.Init(this);
40: }
41: }
42:
43: protected virtual void InitializeComponents(IServiceLocator locator)
44: {
45: }
46:
47: protected virtual void RegisterComponents(IServiceLocator locator)
48: {
49: Type[] webTypes = typeof (HttpContext).Assembly.GetTypes();
50: Type webModule = typeof (IHttpModule);
51:
52: List<Type> moduleTypes = (from moduleType in webTypes
53: where webModule.IsAssignableFrom(moduleType) &&
54: moduleType != webModule &&
55: moduleType.IsPublic
56: select moduleType).ToList();
57:
58: foreach (Type moduleType in moduleTypes)
59: {
60: locator.Register<IHttpModule>(moduleType);
61: }
62: }
63:
64: protected virtual ServiceLocator GetServiceLocator()
65: {
66: return new ServiceLocator();
67: }
68: }
69: }
Also, you must unregister all IHttpModules from within your web.config to prevent double registry:
1: <!-- Remove all modules since we're auto wiring them! -->
2: <httpModules>
3: <clear />
4:
5: <!-- Sadly, this module is not public so we need manually add it -->
6: <add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>
7:
8: <!-- These aren't part of System.Web assembly, so we manually add them -->
9: <add name="ErrorHandlerModule" type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>
10: <add name="ServiceModel" type="System.ServiceModel.Activation.HttpModule, System.ServiceModel, Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>
11: </httpModules>
The important step here is the Application_Start, since this is the method where we register the IHttpModules with the IoC container so they could be used later. In the example above, all we’re doing is registering the modules that come out of the box with ASP.NET.
1: protected virtual void RegisterComponents(IServiceLocator locator)
2: {
3: Type[] webTypes = typeof (HttpContext).Assembly.GetTypes();
4: Type webModule = typeof (IHttpModule);
5:
6: List<Type> moduleTypes = (from moduleType in webTypes
7: where webModule.IsAssignableFrom(moduleType) &&
8: moduleType != webModule &&
9: moduleType.IsPublic
10: select moduleType).ToList();
11:
12: foreach (Type moduleType in moduleTypes)
13: {
14: locator.Register<IHttpModule>(moduleType);
15: }
16: }
The LINQ query is used to get any types that implement IHttpModule excluding the IHttpModule type and any type that’s not public (it’s marked private or internal). However, to get the ones that are not public, you’ll need to manually add them on your web.config (yeah, it sucks). Once you have the modules, we registered them with the container under the IHttpModule service type.
Since more than likely you’ll have your own IHttpModules for your web application you should register them within your application’s Global.asax:
1: namespace WebFormClient
2: {
3: using System.Web;
4: using Common;
5: using Modules;
6:
7: public class Global : AutowireApplication
8: {
9: protected override void RegisterComponents(IServiceLocator locator)
10: {
11: base.RegisterComponents(locator);
12:
13: locator.Register<IHttpModule>(typeof(CustomModule));
14: locator.Register<ModuleDependency, ModuleDependency>();
15: }
16: }
17: }
In this sample, CustomModule is dependent on ModuleDependency which is injected via it’s constructor. So you’ll need to register the module as well its dependency with the container.
Once all this is completed, the magic is done by the override of the Init method within AutowireApplication since it calls the InitializeModules method that performs the actual work of passing the current instance of the application to every module so it can register to their corresponding events.
1: public override void Init()
2: {
3: base.Init();
4:
5: // Register all system components
6: InitializeModules(ServiceLocator);
7: InitializeComponents(ServiceLocator);
8: }
9:
10: protected virtual void InitializeModules(IServiceLocator locator)
11: {
12: List<IHttpModule> modules = locator.ResolveServices<IHttpModule>();
13: if (modules == null) return;
14:
15: foreach (IHttpModule module in modules)
16: {
17: module.Init(this);
18: }
19: }
20:
21: protected virtual void InitializeComponents(IServiceLocator locator)
22: {
23: }
If you run the sample WebForms client (it will be the same for MVC), you’ll see the output from the application:
That’s it! You’ve successfully changed ASP.NET to run with IoC for most of its IHttpModules!
Feel free to check out the code and use as necessary, and like always if you have any questions or feedback, leave a comment! :)