HTTP module is a .NET Framework class that implements the IHttpModule interface, they called as ISAPI filter in IIS stack so modules are counterpart in ASP.NET, have ability to intercept and handle system events and other modules events. Based on the web.config file, per application basis, HTTP modules can filter the raw data, the context, within the request.
Note: all ASP.NET applications inherit a gang of system modules by default as configured in the global web.config
Before we get in to the details of internals let us understand IHttpModule interface and HTTPApplication or ASP.NET Application object first.
ASP.NET Application object (HTTPApplication)
In classic pipeline mode an ASP.NET request is handed over to an ISAPI extension right after IIS has obtained an authentication token for the sender. An ISAPI extension, HTTP Handler is ASP.NET counterpart, is a Win32 DLL that gets loaded into the IIS worker process that's in charge for any given Web application. The ISAPI extension will load the CLR in the worker process and launch the ASP.NET runtime pipeline to actually process the request.
In the ASP.NET pipeline, the request life cycle is governed by a static instance of the HttpRuntime class. A single instance of the HttpRuntime class exists per application, and it's created when the first request for the application comes in. When the HttpRuntime object is takes over the control to process a request, it performs a number of initialization tasks the first one is the creation of the HTTP context object (which contains the ASP.NET intrinsic objects like Response, Request, Session and many more) and followed by creation of an ASP.NET application object to carry out the request.
So by now we have Worker Process, CRL up and running, HttpRuntime object, HttpContext object, HttpApplication object. ASP.NET application object uses newly created HttpContext object to complete the request. Now given ASP.NET application object let us see where and all and what and all we can do.
Don't forget "many more" we will see them. They are the concern of this topic. Before that, let us see what IHttpModule is first.
IHttpModule interface
public interface IHttpModule
{
void Dispose();
void Init(HttpApplication context);
}
The IHttpModule interface has two methods namely Init and Dispose. Any class that inherits from IHttpModule needs to provide the implementation for these methods. As we all know that Dispose method takes care of resource cleanup typically closing DB connection, any handlers to the costly disk resources. We have lest concern about this method. Let us see what can, or have to, do with the Init method.
Init method, has one parameter that is HttpApplication Object, at first initializes the http module and prepares for handling requests. It receives the HttpApplication object reference when it executes. As said now http module has the access to all intrinsic objects and many more.
Now we will see what is 'many more', the HttpApplication object has many events in it, below is the list. These events can be wired up in the HttpModule and providing the behavior for those events. So by using HttpModule we can bypass or customize the behavior for the application by wiring up these events in the custom modules.
Event | Description |
AcquireRequestState, PostAcquireRequestState | Occurs when the handler that will actually serve the request acquires the state information associated with the request. |
AuthenticateRequest, PostAuthenticateRequest | Occurs when a security module has established the identity of the user. |
AuthorizeRequest, PostAuthorizeRequest | Occurs when a security module has verified user authorization. |
BeginRequest | Occurs as soon as the HTTP pipeline begins to process the request. |
Disposed | Occurs when the HttpApplication object is disposed of as a result of a call to Dispose. |
EndRequest | Occurs as the last event in the HTTP pipeline chain of execution. |
Error Occurs | when an unhandled exception is thrown. |
LogRequest, PostLogRequest | Occurs when the response has been generated and logging modules can do their work. These events are fired only to applications that run in Integrated pipeline mode under IIS 7. |
MapRequestHandler | Occurs when it is about time to set the handler to serve the request. This event is fired only to applications that run in Integrated pipeline mode under IIS 7. |
PostMapRequestHandler | Occurs when the HTTP handler to serve the request has been found. |
PostRequestHandlerExecute | Occurs when the HTTP handler of choice finishes execution. The response text has been generated at this point. |
PreRequestHandlerExecute | Occurs just before the HTTP handler of choice begins to work. |
PreSendRequestContent | Occurs just before the ASP.NET runtime sends the response text to the client. |
PreSendRequestHeaders | Occurs just before the ASP.NET runtime sends HTTP headers to the client. |
ReleaseRequestState, PostReleaseRequestState | Occurs when the handler releases the state information associated with the current request. |
ResolveRequestCache, PostResolveRequestCache | Occurs when the ASP.NET runtime resolves the request through the output cache. |
UpdateRequestCache, PostUpdateRequestCache | Occurs when the ASP.NET runtime stores the response of the current request in the output cache to be used to serve subsequent requests. |
Now let us implement and see the HttpModule in action. For the implementation I am using trail version of Microsoft Visual Studio 2010 Ultimate. Follow the steps to create the sample model in ASP.NET application.
1. Open up VSTS and create an ASP.NET Web Application using default template. Names of solution and web application at your convenience. By default application is created with many folders in it, I am deleting all of them including default page for ease purpose and web.config entries.
2. Add an ASP.NET Module to the solution. And it looks like below.
using System;
using System.Web;
namespace SampleModelule
{
public class SampleModule
: IHttpModule
{
/// <summary>
/// You will need to configure this module in the web.config
file of your
/// web and register it with IIS before being able to use it.
For more information
/// see the following link:
http://go.microsoft.com/?linkid=8101007
/// </summary>
#region IHttpModule Members
public void Dispose()
{
//clean-up
code here.
}
public void Init(HttpApplication
context)
{
// Below
is an example of how you can handle LogRequest event and provide
// custom
logging implementation for it
context.LogRequest += new EventHandler(OnLogRequest);
}
#endregion
public void OnLogRequest(Object
source, EventArgs e)
{
//custom
logging logic can go here
}
}
}
3. Clean up the default code and add the following simple code.
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(OnBeginRequest);
context.EndRequest += new EventHandler(OnEndRequest);
}
void OnEndRequest(object
sender, EventArgs e)
{
}
void OnBeginRequest(object
sender, EventArgs e)
{
}
4. The events handlers BeginRequest & EndRequest have a similar signature. They obtain a reference to the current HttpApplication object from the sender and get the access to the all the intrinsic objects from there. Next, they work with the Response object to append custom text to the response header.
5. Add the following code to the methods.
public void Init(HttpApplication context)
{
context.BeginRequest += new EventHandler(OnBeginRequest);
context.EndRequest += new EventHandler(OnEndRequest);
}
void OnEndRequest(object
sender, EventArgs e)
{
HttpApplication
appObject = (HttpApplication)sender;
HttpContext
contextObject = appObject.Context;
// PageHeaderText
is a constant string defined elsewhere
contextObject.Response.Write("<table width='100%'
border='2'><tr><td align='center' background='Gray'>The last
event in the HTTP pipeline chain of
execution</td></tr></table>");
}
void OnBeginRequest(object
sender, EventArgs e)
{
HttpApplication
appObject = (HttpApplication)sender;
HttpContext
contextObject = appObject.Context;
// Add custom
header to the HTTP response
contextObject.Response.AppendHeader("Author", "Chinna
Srihari");
// PageHeaderText
is a constant string defined elsewhere
contextObject.Response.Write("<table width='100%'
border='2'><tr><td align='center' background='Gray'>HTTP
pipeline begins to process the
request</td></tr></table>");
}
Now we have the module is ready to use and the time for registering the module. To register any module we should know the scope. If any module that is being used should use across all the ASP.NET applications the go for the global web.config this change will affect all the applications, ASP.NET, in the server. Example is authentications. In any case develop and test the module in any one of applications first. For this reason use the web.config file.
Following is the section we will be using to deploy the HttpModule in ASP.NET applications.
<system.web>
<httpModules>
<add name="MyFirstModule"
type="Chinna.SampleModules.SampleModule, SampleModuleAssembly"/>
</httpModules>
</system.web>
Name --> any name that you feel publically used
Type --> it has got two things, fully qualified class name and asseblyname by comma separated.
That's it. Just run the application and you see as below.
Now let us see what else we can do more, URL Rewriting.
Let us understand. From the browser we will be requesting a page to process and display. But there must be other page that actually serves the request, but at the browser the original URL remains same. This kind of behavior or functionality is achieved by using HTTPContext Object's RewriteUrl method.
See the below example, the same above code I am using. Add a new page to the application called WebForm1.aspx. Add the some text like "<h2>Chinna! this is routed page, not your original page</h2>". Whereas we have different text on the Default.aspx "<h2>Chinna! this is routed page, not your original page</h2>"
Just add the below code to your at the end you your OnBeginrequest method.
contextObject.RewritePath("~/WebForm1.aspx");
So you method becomes like
void OnBeginRequest(object
sender, EventArgs e)
{
HttpApplication appObject = (HttpApplication)sender;
HttpContext
contextObject = appObject.Context;
// Add custom
header to the HTTP response
contextObject.Response.AppendHeader("Author", "Chinna
Srihari");
// PageHeaderText
is a constant string defined elsewhere
contextObject.Response.Write("<table width='100%' border='2'><tr><td
align='center' background='Gray'>HTTP pipeline begins to process the
request</td></tr></table>");
contextObject.RewritePath("~/WebForm1.aspx");
}
Now run the application, see the screen below
Note: there is a new model in VSTS 2008 with SP1, but still this works
Hope this helps.