Global Events

Master the Global.asax File

ControlFreak

LANGUAGES: VB.NET | C#

ASP.NET VERSIONS: 2.0+

 

Global Events

Master the Global.asax File

 

By Steve C. Orr

 

HTTP Modules and HTTP Handlers have recently come into style, providing multifaceted solutions for potentially complex development needs that go beyond basic page requests.

 

IIS 7.0 is also highly extensible, providing useful hooks for nearly every conceivable incoming Web request. For those developers lucky enough to have access to it on their deployed servers, it can be a valuable tool indeed.

 

With such tempting solutions at hand, many developers have all but forgotten about the previously beloved Global.asax file. When needs are simple (as is usually the case), the undervalued Global.asax file should still be as treasured every bit as much as it used to be.

 

With this in mind, it s time to revisit the Global.asax file and review how it can fulfill the everyday requirements of modern Web sites.

 

In the Beginning...

When an ASP.NET application receives its first page request, the application s code is compiled (unless it was already precompiled). The first ASP.NET event fired is the Application_Start event. Code can be attached to this event via the Global.asax file to initialize application-wide resources. The Global.asax file may be added to a Web application much like any other file, via the Visual Studio Add New Item dialog box (see Figure 1).

 


Figure 1: Add a Global.asax file to your Web application to intercept global events.

The following Global.asax code can be used to track a Web application s startup date and time:

 

Sub Application_Start(ByVal sender As Object, _

                     ByVal e As EventArgs)

   ' Code that runs on application startup

   Application("StartTime") = Date.Now()

End Sub

 

In addition to the previously mentioned Application_Start event, a Global.asax file freshly added via Visual Studio contains four other events: Application_End, Application_Error, Session_Start, and Session_End (see Figure 2).

 


Figure 2: The contents of a freshly added Global.asax file.

 

The Application_End event is typically fired when an ASP.NET application is shutting down. It can be a good opportunity to deallocate resources that were initialized in the Application_Start event. However, be forewarned that this event is not always reliably fired. Some circumstances can cause this event to be skipped, such as a sudden hard server reboot or clicking the stop button while debugging in the Visual Studio development environment.

 

The Session_Start and Session_End events are similar to the Application_Start and Application_End events, except they apply to each site visitor instead of the overall application. The Session_Start event is fired each time a visitor comes to your site. The Session_End event is fired when a user s session times out (or is manually ended via code, such as the Session.Abandon method). You can still access the user s fleeting session state variables from within the Session_End event. Once again, there are some circumstances that can cause the Session_End event to be skipped. For example, if session state has been reconfigured to use SQL Server or a state server for storage, the Session_End event will not be fired.

 

The code in Figure 3A uses the Session_Start and Session_End events to track how many users are currently using the Web application. Figure 3B shows the equivalent C# code. You should keep in mind that a user s session doesn t typically end until it times out. This configurable time period is 20 minutes (for .NET 1.x applications) or 30 minutes (for .NET 2.x+ applications) after their last request by default, although that number is configurable via the web.config file.

 

Sub Session_Start(ByVal sender As Object, _

                 ByVal e As EventArgs)

   ' Code that runs when a new session is started

   Dim Sessions As Integer = 0

   Application.Lock()

   If Application("Sessions") IsNot Nothing Then

       Sessions = CInt(Application("Sessions"))

   End If

   Sessions += 1

   Application("Sessions") = Sessions

   Application.UnLock()

End Sub

Sub Session_End(ByVal sender As Object, _

               ByVal e As EventArgs)

   ' Code that runs when a session ends.

   Application.Lock()

   Dim Sessions As Integer = CInt(Application("Sessions"))

   Sessions -= 1

   Application("Sessions") = Sessions

   Application.UnLock()

End Sub

Figure 3A: This VB.NET Global.asax code can be used to track how many user sessions are currently active.

 

public void Session_Start(object sender, EventArgs e)

{

   // Code that runs when a new session is started

   int Sessions = 0;

   Application.Lock();

   if (Application["Sessions"] != null)

   {

       Sessions =

           System.Convert.ToInt32(Application["Sessions"]);

   }

   Sessions += 1;

   Application["Sessions"] = Sessions;

   Application.UnLock();

}

public void Session_End(object sender, EventArgs e)

{

   // Code that runs when a session ends.

   Application.Lock();

   int Sessions =

       System.Convert.ToInt32(Application["Sessions"]);

   Sessions -= 1;

   Application["Sessions"] = Sessions;

   Application.UnLock();

}

Figure 3B: This C# Global.asax code can be used to track how many user sessions are currently active.

 

The Application_Error event is a global error handler that can be useful for dealing with unhandled exceptions. It is often used for logging purposes. The following Global.asax code traps any unhandled errors and sends the user to a page designed to deal gracefully with unexpected exceptions:

 

Sub Application_Error(ByVal sender As Object, _

                     ByVal e As EventArgs)

   ' Code that runs when an unhandled error occurs

   Server.Transfer("MyErrorPage.aspx")

End Sub

 

Exploring Hidden Events

In addition to the previously mentioned five events that appear by default in a Global.asax file freshly added through Visual Studio, the HttpApplication class (from which the Global.asax is derived) also supports more than a dozen lesser known events that can be manually added to the Global.asax file.

 

The following HttpApplication-derived events are typically fired for each ASP.NET request. To add one to your Global.asax file you merely need to follow the naming convention of: Application_EventName. As long as this naming convention is followed, the ASP.NET compiler will automatically take care of the rest of the details to ensure it gets wired up properly.

 

ValidateRequest is the first application request event fired. Its purpose is to examine user input for any potentially malicious values. If suspicious values are indeed discovered, an HttpRequestValidationException error is thrown and further request processing is short-circuited.

 

BeginRequest follows in the application event pipeline. It s typically considered to be the first opportunity to examine the incoming request and act upon it if necessary. For example, the code in Figure 4A tracks how many requests have been made since the Web application started. Figure 4B shows the equivalent C# code.

 

Sub Application_BeginRequest(ByVal sender As Object, _

                     ByVal e As EventArgs)

   ' Code that runs before each request

   Dim Requests As Integer = 0

   Application.Lock()

   If Application("Requests") IsNot Nothing Then

       Requests = CInt(Application("Requests"))

   End If

   Requests += 1

   Application("Requests") = Requests

   Application.UnLock()

End Sub

Figure 4A: This VB.NET Global.asax code tracks how many requests have been made since the Web application was last instantiated.

 

public void Application_BeginRequest(object sender,

      EventArgs e)

{

 // Code that runs before each request

 int Requests = 0;

 Application.Lock();

 if (Application["Requests"] != null)

 {

 Requests = System.Convert.ToInt32(Application["Requests"]);

 }

 Requests += 1;

 Application["Requests"] = Requests;

 Application.UnLock();

}

Figure 4B: This C# Global.asax code tracks how many requests have been made since the Web application was last instantiated.

 

AuthenticateRequest is the next application event fired. This event occurs as ASP.NET is attempting to identify the user making the request. You might choose to handle this event to implement your own authentication mechanism.

 

PostAuthenticateRequest is fired after the requesting user has been identified. This is the first opportunity to examine the incoming request if you must also take into account the identity of the user making the request.

 

AuthorizeRequest is then fired. By this point the user has been identified, so now it must be determined if that user should have access to the requested resource or not. You may choose to handle this event to implement your own authorization procedures.

 

PostAuthorizeRequest is only fired if the user has been authorized to access the requested resource. If the user has been determined to have insufficient privileges, this event (and most of the following events) won t typically be fired.

 

ResolveRequestCache is then fired to determine whether the requested resource can be retrieved from the cache or not. This can potentially improve performance.

 

PostResolveRequestCache is executed when ASP.NET bypasses the current event handler and instead allows the request to be retrieved from the cache.

 

AcquireRequestState is executed when ASP.NET is attempting to acquire the current session state of the request. You might choose to handle this request to reconstruct the state with logic of your own creation.

 

PostAcquireRequestState is executed after state has been successfully acquired for the current request. This is the first opportunity to examine the incoming request if you need to also consider the user s current session state.

 

PreRequestHandlerExecute is fired before page event handlers are fired. This event can be used to execute logic just before a page s event handlers are executed. For example: This event is fired, then Page_Init is fired, then Page_Load is fired, etc.

 

PostRequestHandlerExecute is fired just after a page s event handlers are fired. This event can be consumed to examine the request state after a page s event handlers have been executed.

 

ReleaseRequestState is executed when the request s state has been fully processed and is ready to be stored. This event can be used (for example) to modify session state after all of a page s events have been processed. This is your last chance to modify the state before it gets stored in preparation for the next request.

 

PostReleaseRequestState is executed after a request s state has been stored. This event could be used to examine the state that was stored after all of a page s events have been processed. State can no longer be modified in or after this event.

 

UpdateRequestCache is executed after a request so that the content may optionally be cached. This event can be used to modify content before it gets cached. When content is cached, most of these events can be short-circuited for the next incoming request to improve performance.

 

PostUpdateRequestCache is executed after a request s response has been cached. This event may be used to examine exactly what went into the cache.

 

EndRequest is the last event fired in a request s sequence of events. It is always fired even if some of the preceding events have been short-circuited.

 

Adding Custom Events

If custom HTTP Modules are added to an application, they also can raise events. These events also can be handled in the Global.asax file. They simply must be added with the following naming convention: ModuleName_EventName.

 

For example, Forms Authentication is implemented as an HTTP Module that comes with ASP.NET. So to handle its Authenticate method, a handler named FormsAuthentication_Authenticate should be added to the Global.asax file. The code in Figure 5A tracks how many such requests have been made since the application was last instantiated. Figure 5B shows the equivalent C# code.

 

Sub FormsAuthentication_Authenticate(ByVal sender _

      As Object, ByVal e As EventArgs)

   ' Code that runs before each authentication

   Dim Auths As Integer = 0

   Application.Lock()

   If Application("Auths") IsNot Nothing Then

       Auths = CInt(Application("Auths"))

   End If

   Auths += 1

   Application("Auths") = Auths

   Application.UnLock()

End Sub

Figure 5A: This VB.NET Global.asax code tracks how many authentication requests have been made since the Web application was started.

 

public void FormsAuthentication_Authenticate(object

      sender, EventArgs e)

{

 // Code that runs before each authentication

 int Auths = 0;

 Application.Lock();

 if (Application["Auths"] != null)

 {

   Auths = System.Convert.ToInt32(Application["Auths"]);

 }

 Auths += 1;

 Application["Auths"] = Auths;

 Application.UnLock();

}

Figure 5B: This C# Global.asax code tracks how many authentication requests have been made since the Web application was started.

 

Conclusion

The Global.asax file was clearly a vital part of nearly every Web application when ASP.NET 1.0 was released. As ASP.NET has evolved and other development alternatives have come into focus, the Global.asax file has perhaps been buried in the glut of development options available to .NET developers. It is now common to use HTTP Modules, HTTP Handlers, IIS extensions, and other techniques to deal with many of the tasks that used to be the exclusive domain of the Global.asax file. However, that certainly does not mean the Global.asax file is obsolete. In fact, many times perhaps even most of the time it is still the simplest and most concise way to implement central handlers for global Web application events.

 

C# and VB.NET source code accompanying this article is available for download.

 

Steve C. Orr is an ASPInsider, MCSD, Certified ScrumMaster, Microsoft MVP in ASP.NET, and author of Beginning ASP.NET 2.0 AJAX by Wrox. He s been developing software solutions for leading companies in the Seattle area for more than a decade. When he s not busy designing software systems or writing about them, he often can be found loitering at local user groups and habitually lurking in the ASP.NET newsgroup. Find out more about him at http://SteveOrr.net or e-mail him at mailto:[email protected].

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish