Sponsored By Aspose - File Format APIs for .NET

Aspose are the market leader of .NET APIs for file business formats – natively work with DOCX, XLSX, PPT, PDF, MSG, MPP, images formats and many more!

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.

This entry was posted in Grab a coffee before reading. Bookmark the permalink. Follow any comments here with the RSS feed for this post.

29 Responses to Global.asax? Use HttpModules Instead!

  1. redware says:

    Karl. Great article and very clear explanation.

    We use Microsoft CRM and are trying to override the error trapping mechanism they have (probably in the global.asax). Trouble is this apporach is an alternative to the global.asax but how do we stop the existing error handling from running as well.

    I expect somehow we need to stop the module passing it on down the chain. Looking up more now….

  2. Prasad Pimparkar says:

    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.

    http://msdn.microsoft.com/en-us/library/ms178473.aspx.

    Prasad

  3. Steve says:

    Thanks for a good explanation of using httpModules.
    Do you have any samples of using them with Authentication?

  4. Sure you can do it using vb.net codiing

  5. Mathi says:

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

  6. 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.

  7. karl says:

    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.

  8. vikasMisra says:

    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

  9. Jacob says:

    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

  10. karl says:

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

  11. Ken says:

    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.

  12. karl says:

    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.

  13. Ankit Goel says:

    Hi Karl
    this is agreat article.
    Can you please describe the relation and sequence of HTTPHandler, HTTPModule, HttpApplication objects

    Thanks

  14. karl says:

    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 :(

  15. Anonymous says:

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

  16. karl says:

    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 :)

  17. VInny says:

    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?

  18. karl says:

    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.

  19. Prakash Raj says:

    Hi ,

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

  20. karl says:

    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

  21. Dan Sikorsky says:

    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?

  22. karl says:

    use the “App_Code” assembly I think.

    Namespace.Class, App_Code

    Karl

  23. Zoltan Grose says:

    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.

  24. karl says:

    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.

  25. Cameron says:

    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.

  26. Pedrito says:

    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.

  27. karl says:

    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.

  28. Dan Napierski says:

    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

  29. Sonu Kapoor says:

    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