CodeBetter.Com
CodeBetter.Com
RSS 2.0 via Feedburner
           Do you Twitter? Follow us @CodeBetter

Karl Seguin

developer @ Fuel Industries ottawa, ontario

Global.asax? Use HttpModules Instead!

In a previous post, I talked about HttpHandlers - an underused but incredibly useful feature of ASP.NET. Today I want to talk about HttpModules, which are probably more common than HttpHandlers, but could still stand to be advertised a bit more.

HttpModules are incredibly easy to explain, so this will hopefully be a short-ish post. Simply put, HttpModules are portable versions of the global.asax. So, in your HttpModule you'll see things like BeginRequest, OnError, AuthenticateRequest, etc. Actually, since HttpModules implement IHttpModule, you actually only get Init (and Dispose if you have any cleanup to do). The Init method passes in the HttpApplication which lets you hook into all of those events. For example, I have an ErrorModule that I use on most projects:

using System;
using System.Web;
using log4net;

namespace Fuel.Web
{
 public class ErrorModule : IHttpModule
 {
  #region IHttpModule Members
  public void Init(HttpApplication application)
  {
   application.Error += new EventHandler(application_Error);  
  }      
  public void Dispose() { }
  #endregion

  public void application_Error(object sender, EventArgs e)
  {
    //handle error
  }
 }
}

Now, the code in my error handler is pretty simple:

HttpContext ctx = HttpContext.Current;
//get the inner most exception
Exception exception;
for (exception = ctx.Server.GetLastError(); exception.InnerException != null; exception = exception.InnerException) { }
if (exception is HttpException && ((HttpException)exception).GetHttpCode() == 404)
{
 logger.Warn("A 404 occurred", exception);
}
else
{
 logger.Error("ErrorModule caught an unhandled exception", exception);
}


I'm just using a log4net logger to log the exception, if it's a 404 I'm just logging it as a warning.

You can do this just as easily with a global.asax, but those things aren't reusable across projects. That of course means that you'll end up duplicating your code and making it hard to manage. With my ErrorModule class, I just put it in a DLL, drop it in my bin folder and add a couple lines to my web.config under <system.web>:

<httpModules>
 <add  name="ErrorModule" type="Fuel.Web.ErrorModule, Fuel.Web" />
</httpModules>


And voila, I have a global error in place.

In almost all cases, you should go with HttpModules over global.asax because they are simply more reusable. As another example, my localization stuff uses an HttpModule as the basis for adding a multilingual framework to any application. Simply drop the DLL in the bin and add the relevant line in your web.config and you're on your way. Here's the important code from that module:

public void Init(HttpApplication context)
{
 context.BeginRequest += new EventHandler(context_BeginRequest);
}
public void Dispose() {}
private void context_BeginRequest(object sender, EventArgs e)
{
 HttpRequest request = ((HttpApplication) sender).Request;
 HttpContext context = ((HttpApplication)sender).Context;
 string applicationPath = request.ApplicationPath;
 if(applicationPath == "/")
 {
    applicationPath = string.Empty;
 }
 string requestPath = request.Url.AbsolutePath.Substring(applicationPath.Length);
 //just a function that parses the path for a culture and sets the CurrentCulture and CurrentUICulture
 LoadCulture(ref requestPath);
 context.RewritePath(applicationPath + requestPath);
}


If you are developing a shrink-wrap product, you don't have a choice but to use HttpModules, because the last thing you want is to ship a global.asax which the user must use, overwriting the code in his own global.asax.

The only time you want to use Global.asax is when using OutputCaching with the VaryByCustom property. As far as I know, the GetVaryByCustomString function _must_ be placed in the global.asax file.

Anyways, switching from Global.asax to HttpModules is pretty straightforward. So I encourage you to look at where it makes sense (ie, where you see the potential for reuse across applications) and make it so.




Comments

Sonu Kapoor said:

ELMAH (a application-wide error logging module that is completely pluggable) is a pretty good example on HttpModules:

http://www.gotdotnet.com/workspaces/workspace.aspx?id=f18bab11-162c-4267-a46e-72438c38df6f
# June 12, 2006 4:27 PM

Christopher Steen said:

.NET Framework 3.0: Clearing the Confusion [Via: kaevans ]
Global.asax? Use HttpModules Instead! [Via:...
# June 13, 2006 12:53 AM

Jason Haley said:

# June 13, 2006 9:38 AM

Dan Napierski said:

Great article.  The error handling is very clear.  What about tasks that we're doing in Application_Start(...)?  How can we port these to the IHttpModule?  - Thanks, Dan
# June 13, 2006 8:09 PM

karl said:

Dan:
I'm pretty sure you can safely put this code in the init function itself. It fires more or less around the same timeframe as the ApplicationStart for the global.asax - I can't remember which fires first, but the app/site is at the same state in both cases.
# June 13, 2006 8:43 PM

Pedrito said:

Good article.  However, another useful and just as easy to implement solution for creating useful, reusable code at the application level is to create a base class (or hierarchy of them depending on your needs) to sit between your HttpApplication base class and your front end Global classes.

# June 26, 2006 1:57 PM

K. Scott Allen said:

Q: I’m using the default web site project model in Visual Studio 2005. When I add a global.asax file...
# July 4, 2006 10:33 PM

Cameron said:

This is neat.  And thanks for explaining it in a down-to-earth manner!  My only question is we log our errors.  If your error handler has something like ErrorClass.LogError(blah blah); then an HttpModule may not be much more portable than Global.asax because the developer is still going to have database connections and whatnot to configure.
# July 6, 2006 7:29 PM

karl said:

Cameron:
Most stand-alone libraries or whatnot require some type of configuration. It's true that my example here is a little on the slim - so you're only benefiting slightly. It's generally easier to add a custom configuration section and add a few lines of code, than it is to have to rewrite somethign though.
# July 6, 2006 7:37 PM

Zoltan Grose said:

What is the proper syntax for the httpModules add element if you aren't compiling your webapp into a DLL? I tried moving some of my global.asax code into a class but I'm uncertain what the proper syntax would be for, as an example, MyHttpModule.cs in the App_Code folder.
# July 7, 2006 5:37 PM

karl said:

use the "App_Code" assembly I think.

Namespace.Class, App_Code

Karl
# July 7, 2006 11:20 PM

Dan Sikorsky said:

I use a similar HttpModule that handles unhandled exceptions from an .aspx.cs page by logging to a text file, logging to the event viewer's app log, and sending out an email to the website developer and any interested parties. Finally, a user friendly page (GenericError.aspx) is Redirected To.

My Page_Load event has no code, so when the page is unavailable, (for instance, the website is down), an unhandled event is generated and my HttpModule is called. Everything is executed as listed above, but the redirection to GenericError.aspx generates another unhandled exception (because the website is down), and thus we enter into a loop.

I end up with 14000+ email messages, and so does everyone specified in a web.config list.

The only fix I can see is to put a try/catch block in every Page's Page_Load event.

Is there some smarter way to handle this?
# July 18, 2006 12:06 PM

karl said:

Dan:
In your httpmodule, check to see if the requested page is GenericError.aspx, and if so, simply don't redirect, something like:

HttpRequest request = Context.Current.Request;
if (string.Compare(request.LocalPath, "/GenericError.aspx", true))
{
   Context.Current.Response.Write("An unhandled error occurred");
   Context.Current.Response.End();
}
//should be safe to redirect to GenericError.aspx
# July 18, 2006 3:55 PM

Prakash Raj said:

Hi ,


I want Session to be end whenever the user directly closes the IE Browse X button.



# July 22, 2006 8:57 AM

karl said:

Prakash: this isn't possible using HttpHandlers, mostly 'cuz the close "event" is a client-side behaviour. Your best bet is to hook into the onbeforeunload javascript event (which is only supported in IE I think) and try to clean up there (possibly be opening up a very small window window.open("terminate.aspx") or something like it). It won't be the most reliable, but I'm pretty sure it's your only solution.
# July 22, 2006 10:59 AM

VInny said:

Good alternative to the Global.asax, however I am trying to redirect user to a friendly error page after the error is handled. However, Response.Redirect is not an option from the HttpModule, how were you doing it?

# November 6, 2006 5:13 PM

karl said:

Vinny:

As you can see from my example, the sender parameter to the function is actually an HttpApplication object. From it you can get access to the Response object (the same way I got access to the Request object) and redirect away :)

# November 6, 2006 6:23 PM

Anonymous said:

If you need Session_Start and/or Session_End events, then you still need Global.asax, isn't that right?

# January 20, 2007 3:01 AM

karl said:

There's a way to hook into those events, but as far as I know, it's something of a nightmare. People have a really hard time doing it. It's something like (which you place in your Init):

HttpModuleCollection modules = application.Modules;

SessionStateModule module = modules["Session"] as SessionStateModule;

if (module != null)

{

 stateModule.Start += (new EventHandler (this.Session_Start));

 stateModule.End += (new EventHandler(this.Session_End));

}

The problem, as I understand it, is that there are multiple SessionStateModules loaded, and you need to find the right one for the start and end events. Also, I believe your own httpmodule fires multiple times and you need to find the right one of those too. I have no clue which  the "right one" is though :(

# January 20, 2007 8:41 AM

Ankit Goel said:

Hi Karl

this is agreat article.

Can you please describe the relation and sequence of HTTPHandler, HTTPModule, HttpApplication objects

Thanks

# February 21, 2007 1:07 AM

karl said:

Ankit:

A key point is that HttpModules fire for all requests, HttpHandlers only for requests matching the path="" attribute of the configuration - and there can only be 1 match.

As best as I know...There's a single HttpApplication instance for a given site. This instance has a collection of all the modules as well as the facilities to determine the correct httphandler to use.

A request comes in, the Application fires off a number of events which your HttpModules can hook into (like BeginRequest). As best I can tell they are fired in the order you put them in, but I wouldn't rely on that. At some point, the correct HttpHandler is determine from the requested URL and ProcessRequest is called. Then a series of other events, such as EndRequest are fired.

# February 21, 2007 6:38 PM

Ken said:

This post was really useful for me. I actually found it on another website where someone cut and pasted it all but I found your comment at the bottom so now I can compliment the right person!

The one thing that was really useful for me was the reference to HttpException.GetHttpCode().

I was writing my own Error Log code and wanted to catch all the exceptions on the Application.Error event, but I didn't want to log 404 and 403 errors. I couldn't figure out how to filter out the unwanted exceptions but now that I've got GetHttpCode everything's great.

Thanks again for posting this. It's made my app that wee bit better.

# February 27, 2007 10:09 AM

karl said:

Ken:

Thanks for the comment. May I ask what website? :)

# February 27, 2007 5:18 PM

Jacob said:

Karl: "As best as I know...There's a single HttpApplication instance for a given site. This instance has a collection of all the modules as well as the facilities to determine the correct httphandler to use."

Karl, I don't believe this is the case. The MSDN help says: "__Instances__ of the HttpApplication class are created in the ASP.NET infrastructure, not by the user directly. One instance of the HttpApplication class is used to process many requests in its lifetime; however, it can process only one request at a time. Thus, member variables can be used to store per-request data."

In order to deal with multiple requests concurrently, ASP.NET would need to create several instances of this class. Take a look at this article for a bug that can occur if you assume there is only one instance of this class in your application

http://odetocode.com/Blogs/scott/archive/2007/02/06/10484.aspx

# March 27, 2007 10:26 AM

vikasMisra said:

i want answers of  questions please answer

1) I have a running site having 2000 pages and suddenly i required to call a certain single function on each page request.

do i need to implement httpmodule for this if yes then how to call that function then do normal processing???

2)i wanna override a base class functon and then after want to call the base class function what will be the way????

3)i wanna call button 2 event handler on button1 event handler

occasionaly

i know all the answers but not sure kindly help me

# May 20, 2007 8:42 AM

karl said:

Vikas

1 - Yes, an HttpModule seems like the right thing in your case. If you hook into the BeginRequest event, you can do whatever needs doing, and then the normal processing will occur.

2 - You can simply call base.Function(params), or in vb.net use MyBase.Function(), something like:

public void override DoSomething(int value)

{

  //put your overriding cod here;

  base.DoSomething(value);

}

3 - Well, you can hook any button event to any button handler even if something else is already hooked into it, there are really two ways to do this, hook them directly into the same function:

button1.Click += new ....ButtonClick;

button2.Click += new ...ButtonClick;

OR

button1.Click += new .... Button1Click;

button2.click += new ... Button2Click;

and then call Button1Click from Button2click...I think the first approach is better.

# May 20, 2007 10:56 AM

Ayende Rahien said:

There is just one big difference that you should be aware of, Application_Start is called multiply times for HttpModule, but only once for Global.asax.

This means that for singleton style stuff, it is easier to use the Global.asax

I have a UnitOfWorkApplication that inherits from HttpApplication that handles this for me, and I just inherit from that in my Global.asax and I am done with it.

# May 20, 2007 6:37 PM

Mathi said:

Can we  do this through vb.net coding ?. If we can then how to write the httphandlers in vb coding

# July 4, 2007 11:15 AM

yogendra thakur said:

Sure you can do it using vb.net codiing

# July 19, 2007 11:31 AM

Steve said:

Thanks for a good explanation of using httpModules.

Do you have any samples of using them with Authentication?

# December 12, 2007 1:09 PM

Prasad Pimparkar said:

This is a great article. Following is the MSDN link which provides overview of the entire ASP.Net application life cycle. It also provides the place where handlers are called.

msdn.microsoft.com/.../ms178473.aspx.

Prasad

# June 22, 2008 1:52 PM

Leave a Comment

(required)  
(optional)
(required)  

Enter the numbers above:
Add
Check out Devlicio.us!