Global Events
Master the Global.asax File
October 30, 2009
ControlFreak
LANGUAGES:VB.NET | C#
ASP.NETVERSIONS: 2.0+
Global Events
Master the Global.asax File
By Steve C. Orr
HTTP Modules and HTTP Handlers have recently come intostyle, providing multifaceted solutions for potentially complex developmentneeds that go beyond basic page requests.
IIS 7.0 is also highly extensible, providing useful hooksfor nearly every conceivable incoming Web request. For those developers luckyenough to have access to it on their deployed servers, it can be a valuabletool indeed.
With such tempting solutions at hand, many developers haveall but forgotten about the previously beloved Global.asax file. When needs aresimple (as is usually the case), the undervalued Global.asax file should stillbe as treasured every bit as much as it used to be.
With this in mind, it s time to revisit the Global.asaxfile and review how it can fulfill the everyday requirements of modern Websites.
In the Beginning...
When an ASP.NETapplication receives its first page request, the application s code is compiled(unless it was already precompiled). The first ASP.NETevent fired is the Application_Start event. Code can be attached to this eventvia the Global.asax file to initialize application-wide resources. TheGlobal.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 toyour Web application to intercept global events.
The following Global.asax code can be used to track a Web application sstartup date and time:
Sub Application_Start(ByVal sender As Object, _
ByVale As EventArgs)
' Code that runs onapplication startup
Application("StartTime") = Date.Now()
End Sub
In addition to the previously mentioned Application_Startevent, a Global.asax file freshly added via Visual Studio contains four otherevents: Application_End, Application_Error, Session_Start, and Session_End (seeFigure 2).
Figure 2: The contents of a freshlyadded Global.asax file.
The Application_End event is typically fired when an ASP.NETapplication is shutting down. It can be a good opportunity to deallocateresources that were initialized in the Application_Start event. However, beforewarned that this event is not always reliably fired. Some circumstances cancause this event to be skipped, such as a sudden hard server reboot or clickingthe stop button while debugging in the Visual Studio development environment.
The Session_Start and Session_End events are similar tothe Application_Start and Application_End events, except they apply to eachsite visitor instead of the overall application. The Session_Start event isfired each time a visitor comes to your site. The Session_End event is fired whena user s session times out (or is manually ended via code, such as theSession.Abandon method). You can still access the user s fleeting session statevariables from within the Session_End event. Once again, there are somecircumstances 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 forstorage, the Session_End event will not be fired.
The code in Figure 3A uses the Session_Start andSession_End events to track how many users are currently using the Webapplication. Figure 3B shows the equivalent C# code. You should keep in mindthat a user s session doesn t typically end until it times out. Thisconfigurable time period is 20 minutes (for .NET1.x applications) or 30 minutes (for .NET2.x+ applications) after their last request by default, although that number isconfigurable via the web.config file.
Sub Session_Start(ByVal sender As Object, _
ByVal eAs EventArgs)
' Code that runs when anew session is started
Dim Sessions As Integer= 0
Application.Lock()
IfApplication("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 AsEventArgs)
' Code that runs when asession ends.
Application.Lock()
Dim Sessions As Integer= CInt(Application("Sessions"))
Sessions -= 1
Application("Sessions") = Sessions
Application.UnLock()
End Sub
Figure 3A: ThisVB.NET Global.asax code can be used to trackhow many user sessions are currently active.
public void Session_Start(object sender, EventArgs e)
{
// Code that runs whena 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 whena 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 currentlyactive.
The Application_Error event is a global error handler thatcan be useful for dealing with unhandled exceptions. It is often used forlogging purposes. The following Global.asax code traps any unhandled errors andsends the user to a page designed to deal gracefully with unexpectedexceptions:
Sub Application_Error(ByVal sender As Object, _
ByVale As EventArgs)
' Code that runs whenan unhandled error occurs
Server.Transfer("MyErrorPage.aspx")
End Sub
Exploring Hidden Events
In addition to the previously mentioned five events thatappear by default in a Global.asax file freshly added through Visual Studio,the HttpApplication class (from which the Global.asax is derived) also supportsmore than a dozen lesser known events that can be manually added to theGlobal.asax file.
The following HttpApplication-derived events are typicallyfired for each ASP.NET request. To add oneto your Global.asax file you merely need to follow the naming convention of: Application_EventName. As long as this namingconvention is followed, the ASP.NET compilerwill automatically take care of the rest of the details to ensure it gets wiredup properly.
ValidateRequest is the first application request eventfired. Its purpose is to examine user input for any potentially maliciousvalues. If suspicious values are indeed discovered, anHttpRequestValidationException error is thrown and further request processingis short-circuited.
BeginRequest follows in the application event pipeline. It stypically considered to be the first opportunity to examine the incomingrequest and act upon it if necessary. For example, the code in Figure 4A trackshow many requests have been made since the Web application started. Figure 4Bshows the equivalent C# code.
Sub Application_BeginRequest(ByVal sender As Object, _
ByVale As EventArgs)
' Code that runs beforeeach request
Dim Requests As Integer= 0
Application.Lock()
IfApplication("Requests") IsNot Nothing Then
Requests =CInt(Application("Requests"))
End If
Requests += 1
Application("Requests") = Requests
Application.UnLock()
End Sub
Figure 4A: ThisVB.NET Global.asax code tracks how manyrequests have been made since the Web application was last instantiated.
public void Application_BeginRequest(object sender,
EventArgs e)
{
// Code that runs beforeeach 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 Webapplication was last instantiated.
AuthenticateRequest is the next application event fired. Thisevent occurs as ASP.NET is attempting toidentify the user making the request. You might choose to handle this event toimplement your own authentication mechanism.
PostAuthenticateRequest is fired after the requesting userhas been identified. This is the first opportunity to examine the incomingrequest if you must also take into account the identity of the user making therequest.
AuthorizeRequest is then fired. By this point the user hasbeen identified, so now it must be determined if that user should have accessto the requested resource or not. You may choose to handle this event to implementyour own authorization procedures.
PostAuthorizeRequest is only fired if the user has beenauthorized to access the requested resource. If the user has been determined tohave insufficient privileges, this event (and most of the following events) won ttypically be fired.
ResolveRequestCache is then fired to determine whether therequested resource can be retrieved from the cache or not. This can potentiallyimprove performance.
PostResolveRequestCache is executed when ASP.NETbypasses the current event handler and instead allows the request to beretrieved from the cache.
AcquireRequestState is executed when ASP.NETis attempting to acquire the current session state of the request. You mightchoose to handle this request to reconstruct the state with logic of your owncreation.
PostAcquireRequestState is executed after state has beensuccessfully acquired for the current request. This is the first opportunity toexamine the incoming request if you need to also consider the user s currentsession state.
PreRequestHandlerExecute is fired before page eventhandlers are fired. This event can be used to execute logic just before a page sevent handlers are executed. For example: This event is fired, then Page_Initis fired, then Page_Load is fired, etc.
PostRequestHandlerExecute is fired just after a page sevent handlers are fired. This event can be consumed to examine the requeststate after a page s event handlers have been executed.
ReleaseRequestState is executed when the request s statehas been fully processed and is ready to be stored. This event can be used (forexample) to modify session state after all of a page s events have beenprocessed. This is your last chance to modify the state before it gets storedin preparation for the next request.
PostReleaseRequestState is executed after a request sstate has been stored. This event could be used to examine the state that wasstored after all of a page s events have been processed. State can no longer bemodified in or after this event.
UpdateRequestCache is executed after a request so that thecontent may optionally be cached. This event can be used to modify contentbefore it gets cached. When content is cached, most of these events can beshort-circuited for the next incoming request to improve performance.
PostUpdateRequestCache is executed after a request sresponse has been cached. This event may be used to examine exactly what wentinto the cache.
EndRequest is the last event fired in a request s sequenceof events. It is always fired even if some of the preceding events have beenshort-circuited.
Adding Custom Events
If custom HTTP Modules are added to an application, theyalso can raise events. These events also can be handled in the Global.asaxfile. They simply must be added with the following naming convention: ModuleName_EventName.
For example, Forms Authentication is implemented as anHTTP Module that comes with ASP.NET. So tohandle its Authenticate method, a handler namedFormsAuthentication_Authenticate should be added to the Global.asax file. Thecode in Figure 5A tracks how many such requests have been made since theapplication was last instantiated. Figure 5B shows the equivalent C# code.
Sub FormsAuthentication_Authenticate(ByVal sender _
As Object, ByVal eAs EventArgs)
' Code that runs beforeeach authentication
Dim Auths As Integer =0
Application.Lock()
IfApplication("Auths") IsNot Nothing Then
Auths =CInt(Application("Auths"))
End If
Auths += 1
Application("Auths") = Auths
Application.UnLock()
End Sub
Figure 5A: ThisVB.NET Global.asax code tracks how manyauthentication requests have been made since the Web application was started.
public void FormsAuthentication_Authenticate(object
sender, EventArgs e)
{
// Code that runs beforeeach 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 sincethe Web application was started.
Conclusion
The Global.asax file was clearly a vital part of nearlyevery Web application when ASP.NET 1.0 wasreleased. As ASP.NET has evolved and otherdevelopment alternatives have come into focus, the Global.asax file has perhapsbeen buried in the glut of development options available to .NETdevelopers. It is now common to use HTTP Modules, HTTP Handlers, IISextensions, and other techniques to deal with many of the tasks that used to bethe exclusive domain of the Global.asax file. However, that certainly does notmean the Global.asax file is obsolete. In fact, many times perhaps even mostof the time it is still the simplest and most concise way to implementcentral handlers for global Web application events.
C# and VB.NET source code accompanying this article isavailable for download.
Steve C. Orr is anASPInsider, MCSD, Certified ScrumMaster, Microsoft MVP in ASP.NET,and author of Beginning ASP.NET 2.0 AJAXby Wrox. He s been developing software solutions for leading companies in the Seattlearea for more than a decade. When he s not busy designing software systems orwriting about them, he often can be found loitering at local user groups andhabitually lurking in the ASP.NET newsgroup.Find out more about him at http://SteveOrr.netor e-mail him at mailto:[email protected].
About the Author
You May Also Like