Home > Articles > Programming > ASP .NET

  • Print
  • + Share This
This chapter is from the book

This chapter is from the book

Using HTTP Handlers and Modules

In the following sections, you learn how to create custom HTTP handlers and modules. Both HTTP handlers and modules enable you to gain low-level access to HTTP requests and responses.

Later, you learn how to implement a simple statistics application for your Web site by using a custom HTTP handler and module.

Working with HTTP Handlers

An HTTP handler enables you to handle all requests made for a file with a certain extension, path, or request type. You can use HTTP handlers to handle requests for ASP.NET pages or any other file type, such as image or text files.

Typical uses for handlers include implementations of custom authentication schemes and custom filters. For example, you can create a handler that authenticates requests for image files. Or you can create a handler that automatically transfers requests for one file to another file.

Classic ASP

HTTP handlers perform many of the same functions in the ASP.NET framework as ISAPI extensions performed in traditional Active Server Pages programming.

All requests made to an ASP.NET Web site are serviced by an HTTP handler. For example, by default, all requests made for files with the extension .aspx are handled by the PageHandlerFactory class. This class produces instances of the PageHandler class to service each request for an ASP.NET page.

To create your own HTTP handler, complete the following steps:

  1. Create a class that implements the IHttpHandler interface.

  2. Add a reference to your HTTP handler in the Web.Config file.

Now, create a simple HTTP handler that retrieves product information from a database table. The handler will take a URL like this:

http://yourSite.com/products/product1.aspx

It will retrieve information for that product from the Products database table.

The first step is to implement the IHttpHandler interface, which requires you to implement one property and one method: IsReusable and ProcessRequest(). The IsReusable property indicates whether the current handler can be reused for another request. The ProcessRequest() method contains the actual code to be executed in response to the request.

The Visual Basic class in Listing 15.15 implements the products handler.

Listing 15.15 ProductsHandler/ProductsHandler.vb

Imports System.Data
Imports System.Data.SqlClient
Imports System.Web

Public Class ProductsHandler
Implements IHttpHandler
 Public Sub ProcessRequest( objContext As HttpContext ) _
 Implements IHttpHandler.ProcessRequest
  Dim intProductID As Integer
  Dim conNorthwind As SqlConnection
  Dim strSelect As String
  Dim cmdSelect As SqlCommand
  Dim dtrProducts As SqlDataReader

  intProductID = GetProductID( objContext.Request.Path ) 
  conNorthwind = New SqlConnection( _
   "Server=localhost;UID=sa;PWD=secret;Database=Northwind" )
  strSelect = "Select ProductName, UnitPrice From Products" & _
   " Where ProductID=@ProductID"
  cmdSelect = New SqlCommand( strSelect, conNorthwind )
  cmdSelect.Parameters.Add( "@ProductID", intProductID )
  conNorthwind.Open()
  dtrProducts = cmdSelect.ExecuteReader( CommandBehavior.SingleRow )
  If dtrProducts.Read Then
   objContext.Response.Write( "<h2>Product Name:</h2>" )
   objContext.Response.Write( dtrProducts( "ProductName" ) )
   objContext.Response.Write( "<h2>Product Price:</h2>" )
   objContext.Response.Write( String.Format( _
    "{0:c}", _
    dtrProducts( "UnitPrice" ) ) )
  End If
  conNorthwind.Close()
 End Sub

 ReadOnly Property IsReusable() As Boolean _
 Implements IHttpHandler.IsReusable
  Get
   Return True
  End Get
 End Property

 Function GetProductID( strPath As String ) As Integer
  Dim intCounter As Integer
  Dim strNumbers As String

  For intCounter = 0 To strPath.Length - 1
   If Char.IsDigit( strPath.Chars( intCounter ) ) Then
    strNumbers &= strPath.Chars( intCounter )
   End If
  Next
  If Not strNumbers = Nothing Then
   Return cINT( strNumbers )
  Else
   Return -1
  End If
 End Function
End Class

The class in Listing 15.15 implements both the ProcessRequest() method and IsReusable property. The ProcessRequest() method uses the GetProductID() function to strip anything but numbers from the page requests. The resulting number is used to retrieve a product with a certain product ID from the Northwind database table. If the product is found, the name and price of the product are displayed (see Figure 15.5).

Figure 15.5 Output of the ProductsHandler.

The IsReusable property simply returns the value True. This is all you need to implement for a simple HTTP handler.

Before you can use the handler, you need to compile it. You can compile the class in Listing 15.15 by executing the following statement from the command line:

vbc /t:library /r:System.dll,System.Data.dll,System.Web.dll ProductsHandler.vb

Next, you need to copy the compiled ProductsHandler class (ProductsHandler.dll) to your application /bin directory.

The last step is to create the proper Web.Config file to associate the handler with a set of pages. The Web.Config file in Listing 15.16 associates the ProductsHandler class with all files requested in the current directory and all its subdirectories.

Listing 15.16 Web.Config

<configuration>
 <system.web>
  <httpHandlers>
   <add verb="*" path="*"
   type="ProductsHandler,ProductsHandler" />
  </httpHandlers>
 </system.web>
</configuration>

The Web.Config file in Listing 15.16 associates the ProductsHandler class with all files. You can limit the files associated with the handler by changing the value of the path attribute. For example, if you want to associate the handler with only those files that have a name starting with product, you would specify the path like this:

<add verb="*" path="product*.*"
type="ProductsHandler,ProductsHandler" />

You can also add multiple entries for a single handler in the Web.Config file.

You should be cautious about one thing: Only certain file extensions are handled by the ASP.NET framework. For example, by default, files with the extension .asp and .html are not handled by the ASP.NET framework. This means that you cannot, by default, create a handler for files with these extensions.

You can associate any file with the ASP.NET framework by modifying the mapping for an extension with the Internet Services Manager. To do so, follow these steps:

  1. Launch the Internet Services Manager.

  2. Right-click the name of your application (virtual directory) and choose Properties.

  3. Choose the Virtual Directory tab and click the Configuration button.

  4. Choose the App Mappings tab and make any desired changes.

You can associate any file extension with the ASP.NET framework by mapping the file extension to the aspnet_isapi.dll file. For example, if you want to write a custom handler for HTML files, you need to map the .html extension to the aspnet_isapi.dll file.

Working with HTTP Modules

An HTTP module is similar to a handler in that it enables you to gain low-level access to the HTTP requests and responses processed by the ASP.NET framework. However, an HTTP module, unlike a handler, enables you to participate in the processing of every request.

The ASP.NET framework includes several standard modules for managing state and implementing authentication schemes. For example, the output cache, session state, forms authentication, and Windows authentication are all implemented as modules.

You can replace any of the standard modules with your own module. For example, if you don't like the way that the ASP.NET framework implements session state, you can replace the standard session state module with one of your own. Or you might decide to extend the ASP.NET framework by creating a completely new module. For example, you might want to implement a custom caching mechanism or create your own custom authentication system.

Creating your own module requires completing the following two steps:

  1. Create a class that implements the IHttpModule interface.

  2. Add a reference to your module to a Web.Config file.

For this next example, create a simple module that implements a custom authentication scheme. The module will require that a parameter named username be passed as part of the parameters collection to a page. If the username parameter is not present, you are automatically redirected to a login page.

The first step is to implement the IHttpModule interface. This interface has two required methods: Init and Dispose. Within the Init subroutine, you can initialize any variables that you need in your module and initialize event handlers with the hosting application. The Dispose event cleans up any of your module's instance variables.

The module in Listing 15.17 implements both of these required methods. (You can find this module in the ModuleAuth subdirectory on the CD-ROM that accompanies this book.)

Listing 15.17 ModuleAuth/AuthModule.vb

Imports System
Imports System.Web
Imports Microsoft.VisualBasic

Namespace myModules

Public Class AuthModule
Implements IHttpModule

Public Sub Init( ByVal myApp As HttpApplication ) _
Implements IHttpModule.Init
 AddHandler myApp.AuthenticateRequest, AddressOf Me.OnEnter
 AddHandler myApp.EndRequest, AddressOf Me.OnLeave
End Sub

Public Sub Dispose() _
Implements IHttpModule.Dispose
End Sub

Public Sub OnEnter( s As Object, e As EventArgs )
 Dim objApp As HttpApplication
 Dim objContext As HttpContext
 Dim strPath As String

 objApp = CType( s, HttpApplication )
 objContext = objApp.Context
 strPath = objContext.Request.Path.ToLower()

 If Right( strPath, 10 ) <> "login.aspx" Then
  If objContext.Request.Params( "username" ) = Nothing Then
   objContext.Response.Redirect( "login.aspx" )
  End If
 End If
End Sub

Public Sub OnLeave( s As Object, e As EventArgs)
End Sub

End Class
End Namespace

The file in Listing 15.17 contains the code for a module named AuthModule. In the Init subroutine, the OnEnter subroutine is associated with the Application_Authenticate event, and the OnLeave subroutine is associated with the Application_EndRequest event.

NOTE

You don't actually use the OnLeave subroutine in the AuthModule module. Typically, you use OnLeave to clean up any resources that you have allocated for your module.

All the work happens in the OnEnter subroutine, which checks whether the current page is the login.aspx page. If it's not, and the parameters collection does not contain a username parameter, the user is automatically redirected to the login.aspx page.

So, if you request a page named secret like this

http://yourSite.com/secret.aspx

you are automatically redirected to the login.aspx page. However, if you request the secret page like this

http://yoursite.com/secret.aspx?username=bob

you can see the secret content in secret.aspx.

You need to compile the file in Listing 15.17 before you can use AuthModule module. To do so, execute the following command from the command line:

vbc /t:library /r:System.dll,System.Web.dll AuthModule.vb

After you compile the module, you need to move the AuthModule.dll file to your application's /bin directory.

Before you can use the module, you must add a reference to the module in the Web.Config file. The Web.Config file in Listing 15.18 contains the needed configuration settings.

Listing 15.18 Web.Config

<configuration>
 <system.web>
  <httpModules>
  <add name="AuthModule"
   type="myModules.AuthModule,AuthModule" />
  </httpModules>
 </system.web>
</configuration>

The Web.Config file in Listing 15.18 includes an httpModules section that contains the custom AuthModule module. The value of the type attribute is the name of the class and the name of the assembly.

Creating the WhosOn Application

In the following sections, you build a simple statistics application called WhosOn that uses both an HTTP handler and module. The WhosOn application displays statistics on the users of a particular page. For example, if your application contains a page at the path /myApp/Products.aspx, you can view statistics on the users who last accessed the page by requesting the page at /myApp/Products.axd (see Figure 15.6).

Figure 15.6 Output of the WhosOn statistics application.

The WhosOn page lists the time, browser type, referrer, and remote IP address for the users who last accessed the page. You can configure the number of users that the application should track per page by modifying a setting in the Web.Config file.

To create the WhosOn application, you need to create three files:

  • Web.Config—You use the Web.Config file to configure both WhosOnModule and WhosOnHandler.

  • WhosOnModule—The WhosOn module executes every time a user requests a page. The module records information on the request by adding the information to the application cache.

  • WhosOnHandler—The WhosOn handler displays statistics for a particular page. For example, requesting products.axd displays statistics on the products.aspx page.

You learn how to create each of these files in the following sections.

NOTE

You can find all the WhosOn files in the WhosOn subdirectory on the CD that accompanies this book.

Creating the WhosOn Web.Config File

You can start this example by creating the Web.Config file contained in Listing 15.19.

Listing 15.19 Web.Config

<configuration>
 <system.web>
  <httpModules>
  <add name="WhosOnModule"
   type="WhosOn.WhosOnModule,WhosOnModule" />
  </httpModules>
  <httpHandlers>
   <add verb="*" path="*.axd"
   type="WhosOn.WhosOnHandler,WhosOnHandler" />
  </httpHandlers>
 </system.web>
 <appSettings>
  <add key="whoson" value="5" />
 </appSettings>
</configuration>

The Web.Config file in Listing 15.19 adds the WhosOnModule to the current application. It associates the WhosOnHandler with all pages that end with the extension .axd. Finally, it sets the maximum number of entries tracked by WhosOnModule to the value 5.

Creating the WhosOn Module

The WhosOnModule, contained in Listing 15.20, grabs statistics for each request and adds them to the application cache.

Listing 15.20 WhosOnModule.vb

Imports System
Imports System.Web
Imports System.Collections
Imports System.Configuration
Imports Microsoft.VisualBasic

Namespace WhosOn

Public Class WhosOnModule 
Implements IHttpModule

Public Sub Init( ByVal myApp As HttpApplication ) _
Implements IHttpModule.Init
 AddHandler myApp.BeginRequest, AddressOf Me.OnEnter
End Sub

Public Sub Dispose() _
Implements IHttpModule.Dispose
End Sub

Public Sub OnEnter( s As Object, e As EventArgs )
 Dim objApp As HttpApplication
 Dim objContext As HttpContext
 Dim strPath As String
 Dim colPageStats As Queue
 Dim objStatsEntry As StatsEntry
 Dim intMaxEntries As Integer

 objApp = CType( s, HttpApplication )
 objContext = objApp.Context
 strPath = objContext.Request.Path.ToLower()

 ' Don't keep stats on .axd pages
 If Right( strPath, 4 ) = ".axd" Then
  Exit Sub
 End If 
 strPath = strPath.SubString( 0, InstrRev( strPath, "." ) - 1 )

 ' Get Max Entries From Web.Config
 intMaxEntries = cINT( ConfigurationSettings.AppSettings( "whoson" ) )

 ' Check whether Cache object exists
 colPageStats = CType( objContext.Cache( "whoson_" & strPath ), Queue )
 If colPageStats Is Nothing Then
  colPageStats = New Queue()
 End If

 ' Add Current Request to the Queue
 objStatsEntry = New StatsEntry( _
  objContext.TimeStamp, _
  objContext.Request.Browser.Type, _
  objContext.Request.UserHostName, _
  objContext.Request.ServerVariables( "HTTP_REFERER" ) ) 
 colPageStats.Enqueue( objStatsEntry )

 ' Delete Previous Entries
 If intMaxEntries <> Nothing Then
 While colPageStats.Count > intMaxEntries
  colPageStats.Dequeue()
 End While
 End If

 ' Update the Cache
 objContext.Cache( "whoson_" & strPath ) = colPageStats
End Sub

End Class

Public Class StatsEntry

Public TimeStamp As DateTime
Public BrowserType As String
Public UserHostName As String
Public Referrer As String

Public Sub New( _
 TimeStamp As DateTime, _
 BrowserType As String, _
 UserHostName As String, _
 Referrer As String )
 
 Me.TimeStamp = TimeStamp
 Me.BrowserType = BrowserType
 Me.UserHostName = UserHostName
 Me.Referrer = Referrer
End Sub

End Class

End Namespace

The WhosOn module is triggered by the Application_BeginRequest event. When this event fires, the OnEnter subroutine is executed.

The OnEnter subroutine first checks whether the current page has the extension .axd. Because you don't want to keep statistics on the statistics pages, the subroutine is exited if the current page is a statistics page.

Next, the WhosOn key from the Web.Config appSetting section is retrieved. The value of the WhosOn key is assigned to a variable named intMaxEntries.

The current page statistics are then retrieved from the application cache. The page statistics are represented in a Queue collection.

Next, the OnEnter subroutine grabs the time stamp of the current request, the browser type, the remote host name, and the referrer server variable. This information is stored in a custom class named StatsEntry.

The StatsEntry class is added to the page statistics queue. If the queue contains more than the maximum allowable number of entries, older entries are deleted.

Finally, the page statistics are inserted back into the Cache object.

Before you can use the WhosOnModule, you need to compile it. You can compile the file in Listing 15.20 by executing the following statement from the command line:

vbc /t:library /r:System.dll,System.Web.dll WhosOnModule.vb

After you compile the module, remember to copy the compiled WhosOnModule.dll file to the application /bin directory.

Creating the WhosOn Handler

The WhosOn handler, contained in Listing 15.21, displays the statistics for a particular page. You can view the output of the WhosOn handler by requesting a page with the extension .axd. For example, a request for the products.axd page would display user statistics for a page named products.aspx.

Listing 15.21 WhosOnHandler.vb

Imports System.Web
Imports System.Collections
Imports Microsoft.VisualBasic

Namespace WhosOn

Public Class WhosOnHandler 
Implements IHttpHandler

Public Sub ProcessRequest( objContext As HttpContext ) _
 Implements IHttpHandler.ProcessRequest
 
 Dim colPageStats As Queue
 Dim strPath As String
 Dim objStatsEntry As StatsEntry

 ' Get Page Path
 strPath = objContext.Request.Path
 strPath = strPath.SubString( 0, InstrRev( strPath, "." ) - 1 )

 ' Display the Stats
 colPageStats = CType( objContext.Cache( "whoson_" & strPath ), Queue )
 If Not colPageStats Is Nothing Then
 objContext.Response.Write( "<table border=1 cellpadding=4>" )
 objContext.Response.Write( "<tr><td colspan=4 bgcolor=orange>" )
 objContext.Response.Write( "<b>Who's On</b>" )
 objContext.Response.Write( "</td></tr>" )
 objContext.Response.Write( "<tr colspan=4 bgcolor=#eeeeee>" )
 objContext.Response.Write( "<th>Timestamp</th>" )
 objContext.Response.Write( "<th>Browser Type</th>" )
 objContext.Response.Write( "<th>Remote Address</th>" )
 objContext.Response.Write( "<th>Referrer</th>" )
 objContext.Response.Write( "</td></tr>" )
 For each objStatsEntry in colPageStats
  objContext.Response.Write( "<tr>" )
  objContext.Response.Write( "<td>" & objStatsEntry.TimeStamp & "&nbsp;</td>" )  
  objContext.Response.Write( "<td>" & objStatsEntry.BrowserType & "&nbsp;</td>" )  
  objContext.Response.Write( "<td>" & objStatsEntry.UserHostName & "&nbsp;</td>" )  
  objContext.Response.Write( "<td>" & objStatsEntry.Referrer & "&nbsp;</td>" )  
 Next
 objContext.Response.Write( "</table>" )
 End If 
 End Sub

 ReadOnly Property IsReusable() As Boolean _
 Implements IHttpHandler.IsReusable
  Get
   Return True
  End Get
 End Property

End Class

End Namespace

The WhosOn handler contains one long subroutine named ProcessRequest, which retrieves the item from the Cache object associated with the current page. If this item exists, it is a Queue collection. Next, the subroutine iterates through the contents of the Queue collection, displaying each statistics entry.

Before you can use the WhosOn handler, you need to compile the file in Listing 15.21. You can compile the handler by executing the following statement from the command line:

vbc /t:library /r:System.dll,System.Web.dll,WhosOnModule.dll WhosOnHandler.vb

Notice that you must reference the WhosOnModule.dll assembly because the StatsEntry class is defined there.

After you compile the handler, you need to copy the WhosOnHandler.dll file to your application's /bin directory.

You can test the WhosOn application by requesting any page from your Web site, for example:

http://localhost/default.aspx

Next, request the page using the extension .axd, rather than .aspx:

http://localhost/default.axd

Statistics for the default.aspx page are displayed.

  • + Share This
  • 🔖 Save To Your Account