ASP.NET 2.0 Unleashed

Whenever you request an ASP.NET page, the ASP.NET Framework assigns an instance of the HttpApplication class to the request. This class performs the following actions in the following order:

  1. Raises the BeginRequest event.

  2. Raises the AuthenticateRequest event.

  3. Raises the AuthorizeRequest event.

  4. Calls the ProcessRequest() method of the Page class.

  5. Raises the EndRequest event.

Note

This is not a complete list of HttpApplication events. There are a lot of them!

The entire page execution lifecycle happens during the fourth step. For example, the Page Init, Load, and PreRender events all happen when the Page class ProcessRequest() method is called.

The HttpApplication object is responsible for raising application events. These application events happen both before and after a page is executed.

You might want to handle one of the application events for several reasons. For example, you might want to implement a custom authentication scheme. In that case, you would need to handle the AuthenticateRequest event to identify the user.

Or, you might want to create a custom logging module that tracks the pages that your website users visit. In that case, you might want to handle the BeginRequest event to record the pages being requested.

If you want to handle HttpApplication events, there are two ways to do it. You can create a Global.asax file or you can create one or more custom HTTP Modules.

Creating a Global.asax File

By default, the ASP.NET Framework maintains a pool of HttpApplication objects to service incoming page requests. A separate HttpApplication instance is assigned to each request.

If you prefer, you can create a custom HttpApplication class. That way, an instance of your custom class is assigned to each page request.

You can create custom properties in your derived class. These properties can be accessed from any page, control, or component. You also can handle any application events in your custom HttpApplication class.

You create a custom HttpApplication class by creating a special file named Global.asax in the root of your application. Every application can have one and only one of these files.

For example, the Global.asax file in Listing 25.22 can be used to track the number of page requests made for any page.

Listing 25.22. Global.asax

<%@ Application Language="VB" %> <%@ Import Namespace="System.Data" %> <%@ Import Namespace="System.Data.SqlClient" %> <%@ Import Namespace="System.Web.Configuration" %> <script runat="server"> Private _conString As String Private _con As SqlConnection Private _cmdSelect As SqlCommand Private _cmdInsert As SqlCommand Public Overrides Sub Init() ' initialize connection _conString = WebConfigurationManager.ConnectionStrings("Log").ConnectionString _con = New SqlConnection(_conString) ' initialize select command _cmdSelect = New SqlCommand("SELECT COUNT(*) FROM Log WHERE Path=@Path", _con) _cmdSelect.Parameters.Add("@Path", SqlDbType.NVarChar, 500) ' initialize insert command _cmdInsert = New SqlCommand("INSERT Log (Path) VALUES (@Path)", _con) _cmdInsert.Parameters.Add("@Path", SqlDbType.NVarChar, 500) End Sub Public ReadOnly Property NumberOfRequests() As Integer Get Dim result As Integer = 0 _cmdSelect.Parameters("@Path").Value = Request.AppRelativeCurrentExecutionFilePath Try _con.Open() result = CType(_cmdSelect.ExecuteScalar(), Integer) Finally _con.Close() End Try Return result End Get End Property Private Sub Application_BeginRequest(ByVal sender As Object, ByVal e As EventArgs) ' Record new request _cmdInsert.Parameters("@Path").Value = Request.AppRelativeCurrentExecutionFilePath Try _con.Open() _cmdInsert.ExecuteNonQuery() Finally _con.Close() End Try End Sub </script>

The Global.asax page in Listing 25.23 handles the Application BeginRequest() event. You can handle any application event by following the naming pattern Application_EventName where EventName is the name of the HttpApplication event.

In Listing 25.23, the Application_BeginRequest() handler is used to record the path of the page being requested. A SqlCommand object is used to record the page path to a database table named Log.

The Global.asax file also extends the base HttpApplication class with a custom property named NumberOfRequests. This property retrieves the number of requests made for the page at the current path.

Finally, the Global.asax includes an Init() method that overrides the base HttpApplication's Init() method. In Listing 25.23, the Init() method is used to initialize the SqlConnection and two SqlCommand objects used in the Global.asax file.

The Init() method is called when the class represented by the Global.asax is initialized. It is called only once, when the class is first created.

Warning

The same instance of the HttpApplication object is re-used for multiple page requests (although never for multiple page requests at the same time). Any value that you assign to a property in a Global.asax file is maintained over the multiple page requests.

The page in Listing 25.23 displays the value of the custom property exposed by the Global.asax file (see Figure 25.9). Notice that the ApplicationInstance property is used to refer to the instance of the HttpApplication class associated with the page. Because the Global.asax file is compiled dynamically in the background, any properties that you declare in the Global.asax file are exposed as strongly typed properties.

Figure 25.9. Displaying the NumberOfRequests property.

Listing 25.23. ShowGlobal.aspx

<%@ Page Language="VB" %> <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"> <html xmlns="http://www.w3.org/1999/xhtml" > <head runat="server"> <title>Show Global</title> </head> <body> <form runat="server"> <div> This page has been requested <%= Me.ApplicationInstance.NumberOfRequests %> times! </div> </form> </body> </html>

Creating Custom HTTP Modules

An HTTP Module is a .NET class that executes with each and every page request. You can use an HTTP Module to handle any of the HttpApplication events that you can handle in the Global.asax file.

Behind the scenes, the ASP.NET Framework uses HTTP Modules to implement many of the standard features of the Framework. For example, the ASP.NET Framework uses the FormsAuthenticationModule to implement Forms authentication and the WindowsAuthenticationModule to implement Windows authentication.

Session state is implemented with an HTTP Module named the SessionStateModule. Page output caching is implemented with an HTTP Module named the OutputCacheModule, and the Profile object is implemented with an HTTP Module named the ProfileModule.

When a new instance of an HttpApplication class is created, the HttpApplication loads all of the HTTP Modules configured in the web configuration file. Each HTTP Module subscribes to one or more HttpApplication events. For example, when the HttpApplication object raises its AuthenticateRequest event, the FormsAuthenticationModule executes its code to authenticate the current user.

In this section, we create a simple authentication HTTP Module. The HTTP Module doesn't allow you to request a page unless you include the proper query string with the request. The code for the custom HTTP Module is contained in Listing 25.24.

Listing 25.24. App_Code\QueryStringAuthenticationModule.vb

Imports System Imports System.Web Namespace AspNetUnleashed Public Class QueryStringAuthenticationModule Implements IHttpModule Public Sub Init(ByVal app As HttpApplication) Implements IHttpModule.Init AddHandler app.AuthorizeRequest, AddressOf AuthorizeRequest End Sub Private Sub AuthorizeRequest(ByVal sender As Object, ByVal e As EventArgs) ' Get context Dim app As HttpApplication = CType(sender, HttpApplication) Dim context As HttpContext = app.Context ' If the request is for Login.aspx, exit Dim path As String = context.Request.AppRelativeCurrentExecutionFilePath If String.Compare(path, "~/login.aspx", True) = 0 Then Return End If ' Check for password Dim authenticated As Boolean = False If Not IsNothing(context.Request.QueryString("password")) Then If context.Request.QueryString("password") = "secret" Then authenticated = True End If End If ' If not authenticated, redirect to login.aspx If Not authenticated Then context.Response.Redirect("~/Login.aspx") End If End Sub Public Sub Dispose() Implements IHttpModule.Dispose End Sub End Class End Namespace

The class in Listing 25.25 implements the IHttpModule interface. This interface includes two methods:

  • InitEnables you to subscribe to HttpApplication events.

  • DisposeEnables you to clean up any resources used by the HTTP Module.

In Listing 25.25, the Init() method adds an event handler for the HttpApplication AuthorizeRequest event. When the HttpApplication raises the AuthorizeRequest event, the HTTP Module's AuthorizeRequest() method executes.

The AuthorizeRequest() method checks for a password=secret query string. If the query string does not exist, then the user is redirected to the Login.aspx page (the method also checks whether the user is requesting the Login.aspx page to avoid a vicious circle).

Before you can use the QueryStringAuthenticationModule, you must register the HTTP Module in the web configuration file. The web configuration file in Listing 25.25 includes an <httpModules> section that registers the module.

Listing 25.25. Web.Config

<?xml version="1.0"?> <configuration> <system.web> <httpModules> <add name="QueryStringAuthenticationModule" type="AspNetUnleashed.QueryStringAuthenticationModule"/> </httpModules> </system.web> </configuration>

After you register the HTTP Module, if you attempt to request any page without including the password=secret query string, then you are redirected to the Login.aspx page. (If the Login.aspx page doesn't exist, you receive a 404 - Not Found error message.)

Категории