ASP.NET has a pretty interesting HTTP runtime architecture if you have ever dug into the covers. The basic run-time support has API’s as powerful as ISAPI (in IIS). ASP.NET offers IHttpHandler and IHttpModule interfaces that offer you similar functionality. I will get into each one in a little bit of detail, but at a high level IHttpHandler is analogous to what would be an ISAPI extension in IIS and IHttpModule would be analogous to what a ISAPI filter in IIS. As a matter of fact, each asp.net page (.aspx) you have in your project is essentially an HTTP Handler.

HTTP Handlers:

The “handlers” are nothing but assemblies (class libraries) that implement IHttpHandler and IHttpAsyncHandler interfaces. Essentially, ASP.NET maps each http request to a http handler. The handler in turn enables processing individual url’s or groups of url’s with similar extensions such as .aspx, .asmx, .ascx, .disco, etc. The handler can be synchronous or asynchronous, the earlier does not return until it finished processing and the latter typically launches a separate process.

When you inherit from IHttpHandler handler, you need to implement both the ProcessRequest method (which processes the individual http requests) and the IsReusable property (which specifies if pooling is supported or not). If you have a more complex set of logic then you should look at inheriting IHttpHandlerFactory, as that allows finer control and you can create different handler based on your need. E.g. you can create a separate handler for GET and PUT.

When you are finished building the handler (essentially compiling your assembly), you need to register it to use it. To register a handler essentially you copy the assembly in the bin folder of where you web app is running and create a section in the web.config. (I’ll have a sample in a bit in this post). You might also want to ensure that the HTTP handler’s extension is registered with IIS.

General process of Building a Handler:

  1. Implement the IHttpHandler interface
  2. Handle the ProcessRequest method and IsRuseable property.
  3. Deploy the assembly
  4. Register the handler

Below is an example of a handler that processes request for the files with the extension “.desigeek”. Note, that you do not need to have a physical file present with that extension.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
public class DesigeekHandler : IHttpHandler {
   bool IHttpHandler.IsReusable { 
      get { 
        return false;
      }  
   }

void IHttpHandler.ProcessRequest( HttpContext context ) {
    HttpResponse response = context.Response;  
    
    response.Write( "< html>" );  
    response.Write( "< body>" );  
    response.Write( "< h1> Hello from Desigeek custom handler. < /h1>" );  
    response.Write( "< /body>" );  
    response.Write( "< /html>" );
}

Register the HTTP handler by creating an entry in the web.config:

1
2
3
<httpHandlers\>  
 <add verb\="\*" path\="\*.desigeek" type\="MyHTTPHandler.DesigeekHandler, MyHTTPHandler"/>  
httpHandlers\>

Below is a sample of what the request would look like. Notice the URL requested is “foo.desigeek” (and there is no physical file with that name on the file system. The first screen-shot is of the web application (which basically is one label which is updated with the system time on PageLoad). The second one is where the handler kicks in.

HTTP Module:

HTTP modules are classes that can be configured to run in response to events that fire during the request for an ASP.NET resource (which can be serviced by any handler). An HTTP module is an assembly that implements the IHttpModule interface and handles events. ASP.NET ships with a number of modules out of the box e.g. SessionStateModule is used to supply session state services to an application.

If you check your machine.config you will there are a number of handlers that come with ASP.NET. e.g you might look at something like:

1
2
3
4
5
6
7
8
9
< httpModules>  
    < add name="OutputCache" type="System.Web.Caching.OutputCacheModule, .../>  
    < add name="Session" type="System.Web.SessionState.SessionStateModule, .../>  
    < add name="WindowsAuthentication" type="System.Web.Security.WindowsAuthenticationModule, .../>  
    < add name="FormsAuthentication" type="System.Web.Security.FormsAuthenticationModule .../>  
    < add name="PassportAuthentication" type="System.Web.Security.PassportAuthenticationModule .../>  
    < add name="UrlAuthorization" type="System.Web.Security.UrlAuthorizationModule, .../>  
    < add name="FileAuthorization" type="System.Web.Security.FileAuthorizationModule, .../>  
< /httpModules>

Basically you follow the similar process as HTTP handlers. Once you have created your assembly you deploy it to the bin folder and register your handler. The general process for writing an HTTP module is:

  • Implement the IHttpModule interface.
  • Handle the Init method and register for the events you need.
  • Handle the events.
  • Optionally implement the Dispose method if you have to do cleanup.
  • Register the module in Web.config.

Here is an example which hooks into the BeginRequest and EndRequest events of HttpApplication and adds “DesigeekModule: Begin of Request“ and “DesigeekModule: End of Request“ to the response being send out to the client. The Init() function is where your register the hooks you want.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
public class DesigeekModule : IHttpModule  {
  
  //you register the events you are want to hook in this function
  void IHttpModule.Init( HttpApplication context ) {
       context.BeginRequest += ( new EventHandler( this.App\_BeginRequest ) );  
       context.EndRequest += ( new EventHandler( this.App\_EndRequest ) );  
  }

  private void App\_EndRequest( object source, EventArgs e ) {
      HttpApplication app = (HttpApplication)source;  
      HttpContext context = app.Context;  
      context.Response.Write( "DesigeekModule: END of Request" );
  }  
  
  private void App\_BeginRequest( object source, EventArgs e ) {
      HttpApplication app = (HttpApplication)source;  
      HttpContext context = app.Context;  
      context.Response.Write( "DesigeekModule: BEGIN of Request" );
  }  
  
  public string ModuleName {  
      get {
        return "DesigeekModule";
      }  
  }  
}

You register the HTTP Module by adding the following section in your web.config.

1
2
< httpModules\> < add name\="DesigeekModule" type\="MyHTTPModule.DesigeekModule, MyHTTPHandler"/>  
< /httpModules\>

When you run this (see the screen shots below), you will notice that when I goto the default.aspx I see the label with the date-time and the BEGIN and END strings. Another interesting point, if I get to one of my custom handler (e.g. foo.desigeek) then as shown below I get output from both the HTTP Module and HTTP Handler, which means there are many powerful things you can get by combining these.

Overall, the image below shows graphical representation of the process of a ASP.NET HTTP pipeline. The process starts with a request arriving at IIS. If the requested resource is configured to be handled by the ASP.NET ISAPI Extension, IIS dispatches the request to the unmanaged aspnet_isapi.dll ISAPI Extension. This ISAPI Extension passes off the request to the managed ASP.NET engine. It is important to note that during the request life cycle, one or more HTTP modules may execute, depending on what modules have been registered and what events they have subscribed to. Finally, the ASP.NET engine determines the HTTP handler that is responsible for rendering the content, invoking the handler and returning the generated content back to IIS, which returns it back to the requesting client. If you want to get creative and do some more processing, you can use the HTTP Factory. Remember, an HTTP handler factory is a class that is not directly responsible for rendering the content, but instead is responsible for selecting and returning an HTTP handler instance. This returned HTTP handler instance is then the one that is tasked with rendering the requested resource.

More Information: