Leveraging httpModules for Better ASP.NET Applications

Since Microsoft first introduced a web server, Internet Information Services (IIS), programmers could catch each request to the server with an ISAPI filter. ISAPI filters traditionally have not been a very easy task for most programmers to create. When ASP.NET was released, the concept of custom modules was introduced, which allowed .NET programmers to effectively create similar libraries that could effectively process each request much like an ISAPI filter.

Interesting note: ASP.NET is actually executed through an ISAPI filter in IIS 6.0 and earlier.

One difference between an ISAPI filter and an ASP.NET module is that ISAPI filters process every request made to the web server and an ASP.NET module only processes requests to the specified site and its virtual directories. An ISAPI filter can be configured to only process a particular domain, resource, and so on, but that is not done very often.

IIS 7 does allow custom httpModules to be registered at the server level. This is a great new feature that enables .NET programmers to easily program server-wide application features. ISAPI programming was never an easy task, even for seasoned programmers.

Introduction to httpModules

As mentioned, custom httpModules provide a way to intercept each request being processed by ASP.NET and alter the request or content being sent to the client or to do server-side processing based on the request itself. Modules are used to execute custom logic to alter, log, and process security logic on each request. A registered custom module is executed for each and every request passed through the ASP.NET engine. Any resource registered in IIS to use ASP.NET for processing is processed by a registered custom httpModule. This includes all .aspx, .asmx, or any other file extension registered to use ASP.NET for processing, including images, JavaScript, Cascading Style Sheets, and so forth.

ASP.NET uses a pool of httpApplication objects that allows it to process multiple requests at one time. This is nice because each request operates on its own thread. Once the request has been processed, the httpApplication object is placed back in the pool, providing an optimal performing server. A reference to the request’s httpApplication is passed to each of the module’s event handlers and the Init method.

An httpModule is simply a class that implements the IhttpModule interface. This interface defines two methods that must be implemented in the class: Init and Dispose. Init fires the first time a request is made to the ASP.NET web site. Dispose executes when the application closes and the module is being destroyed.

How httpModules Are Processed

ASP.NET is processed in several steps known as the ASP.NET pipeline. The steps raise events in the application object that can be intercepted by httpModules to perform custom logic. The custom httpModule generally registers event handlers for each of the events in the ASP.NET pipeline that can be used to execute the logic in the module.

Events should be registered in the module’s Init method. In VB.NET, an event handler is registered using the AddHandler keyword syntax; in C#, the event handler is simply associated with the event through the += syntax:

VB.NET

AddHandler context.BeginRequest, AddressOf Me.Application_BeginRequest

C#

context.BeginRequest += Application_BeginRequest;

Each event handler has a signature that accepts two parameters, source As Object and e As EventArgs. The source parameter is actually a reference to the httpApplication object. It can be used to access the current httpContext, which enables you access to the Request and Response objects that can be used to process the request. Any specialized arguments are passed to the event handler through the EventArgs parameter. This would usually be cast to the actual EventArg class type for the event.

The EventArgs parameter is typically an empty value and is therefore not usable in the Init method. It is included because the standard event handler signature is an object and EventArgs, where EventArgs is a collection of parameter values. The events in the ASP.NET pipeline generally will not pass any values, yet the standard event handler signature is followed in case that should change.

ASP.NET HTTP modules are similar to ISAPI filters in that they run for all requests. However, they are written in managed code and are fully integrated with the life cycle of an ASP.NET application. They also only work for the ASP.NET site to which they are registered. In IIS 7, you can install a custom httpModule at the server level to process every request made to the server, not just a site.

ASP.NET uses modules to implement various application features, including forms authentication, caching, session state, and client script services. In each case, when those services are enabled, the module is called as part of a request and performs tasks that are outside the scope of any single page request. Modules can consume application events and can raise events that can be handled in the Global.asax file.

Modules versus Global.asax

Much of what can be written using an httpModule can also be done in the Global.asax file. Global.asax provides hooks into the events of the ASP.NET pipeline just like an httpModule. One advantage Global.asax provides is the capability to add handlers to events not available to modules, like Session_Start and Session_End.

One advantage that custom httpModules have over Global.asax is the ability to compile modules into class libraries that can be used in multiple ASP.NET web sites. The class library can be registered in the Global Assembly Cache (GAC) and even registered in the server’s Machine.config file.

Consider using the Global.asax file when you want to add code to run with application events that you will not reuse in other applications. Global.asax also provides easy access to session events.

A Minimal Custom httpModule

A custom httpModule is a class that implements the IhttpModule interface. This interface specifies two methods that must be defined by the custom module class: Init and Dispose. Init is passed two parameters: a reference to the httpApplication object for the web site and an EventArgs object. It is invoked when the web site receives its first request. Dispose accepts no parameters. It’s called when the site is being recycled or closed and is used to clean up any open resources, such as file handles.

To create a custom module, simply add a new class file to a web site in Visual Studio (see Figure 1). Then implement the IhttpModule interface. The definitions for the two methods can be created by typing Implements IhttpModule (VB) or : IhttpModule (C#) after the class name and pressing the Enter key in Visual Studio. Visual Studio then creates hollow methods for both Init and Dispose, as the following code shows:

VB.NET

Public Class BaseModule

    Implements IHttpModule

 

    Public Sub Dispose() Implements System.Web.IHttpModule.Dispose

 

    End Sub

 

Public Sub Init(ByVal context As System.Web.HttpApplication) Implements

System.Web.IHttpModule.Init

 

    End Sub

 

     End Class

C#

public class BaseModule : IHttpModule

{

  public void Dispose()

    {

 

    }

  public void Init(System.Web.HttpApplication context)

    {

 

    }

}

The httpApplication’s Init method is the hook to place initialization code and assign custom ASP.NET pipeline event handlers. It passes a reference to the httpApplication for the site (source as Object). An httpApplication object provides access to methods and properties common to all processes in an ASP.NET application. There are 22 events that can be handled by a custom httpModule, some of which are new to IIS 7.

Because the httpApplication object is passed to the method as an object, it must be cast to an httpApplication:

VB.NET

Dim application As HttpApplication = CType(source, HttpApplication)

C#

HttpApplication application = (HttpApplication)source;

Once you cast the object, you can access the current context and its members, such as the Request and Response objects.

The Dispose method is used to dispose of any resources used by the module. For example, if a file handle is still open in the module, here is where you would want to close it and remove it from the application’s memory. Some developers use Dispose to remove the event handler registrations, which forces the garbage collection to be handled more explicitly. Typically there is not much that needs to be placed in the Dispose method, but be aware of any resources created in the Init method that need to be processed appropriately.

ASP.NET Page Life Cycle

Each time a resource is requested through the ASP.NET framework, it passes through a series of events before it is completely processed and sent to the browser. These steps are known as the ASP.NET pipeline. By and large, these events occur in a set sequence, but if there is an unhandled error in any of the events, the Error event is raised and processing of the request is stopped.

Each event provides the capability to hook into the processing of a request at various important stages. For example, to adjust the request before the user is authenticated, you can create an event handler for the AuthenticateRequest. To do something just after a user has been authenticated, the PostAuthenticationRequest event can be handled.

Each event has its own functionality and purposes. Depending on where in the processing the event is raised, access to specific objects may not be available. For instance, you cannot access the ViewState in the PostReleaseRequestState event.

Application Event Description
BeginRequest Occurs as the first event in the HTTP pipeline chain of execution when ASP.NET responds to a request.
AuthenticateRequest. Occurs when a security module has established the identity of the user.
PostAuthenticateRequest Occurs when a security module has established the identity of the user and after the AuthenticateRequest event.
AuthorizeRequest Occurs when a security module has verified user authorization.
PostAuthorizeRequest Occurs when the user for the current request has been authorized.
ResolveRequestCache Occurs when ASP.NET finishes an authorization event to let the caching modules serve requests from the cache, bypassing execution of the event handler (e.g., a page or an XML web service).
PostResolveRequestCache After the PostResolveRequestCache event and before the PostMapRequestHandler event, an event handler (which is a page that corresponds to the request URL) is created. When a server is running IIS 7 in Integrated mode and at least .NET Framework version 3.0, the MapRequestHandler event is raised. When a server is running IIS 7 in Classic mode or an earlier version of IIS, this event cannot be handled.
MapRequestHandler Fired right before the handler for processing the request type is specified and assigned to the Context.
PostMapRequestHandler Occurs when ASP.NET has mapped the current request to the appropriate event handler.
AcquireRequestState Occurs when ASP.NET acquires the current state (e.g., session state) that is associated with the current request.
PostAcquireRequestState Occurs when the request state (e.g., session state) that is associated with the current request has been obtained.
PreRequestHandlerExecute Occurs just before ASP.NET starts executing an event handler (e.g., a page or an XML web service).
PostRequestHandlerExecute Occurs when the ASP.NET event handler (e.g., a page or an XML web service) finishes execution.
ReleaseRequestState Occurs after ASP.NET finishes executing all request event handlers. This event causes state modules to save the current state data.
PostReleaseRequestState After the PostReleaseRequestState event is raised, any existing response filters will filter the output.
UpdateRequestCache Occurs when ASP.NET finishes executing a handler in order to let caching modules store responses that will be used to serve subsequent requests from the cache.
PostUpdateRequestCache Occurs when ASP.NET finishes updating caching modules and storing responses that are used to serve subsequent requests from the cache
EndRequest Occurs as the last event in the HTTP pipeline chain of execution when ASP.NET responds to a request.
LogRequest Occurs just before any logging occurs in the request and fires even if an error is raised during processing. This event is supported in IIS 7 Integrated mode and at least .NET Framework 3.0. It is a new feature supported only in .NET 3.5.
PostLogRequest Occurs just after all event handlers for the LogRequest event. This event is supported IIS 7 Integrated mode and at least the .NET Framework 3.0. It is a new feature supported only in .NET 3.5.
Error Occurs when an unhandled exception is thrown.
PreSendRequestHeaders Fired by the httpApplication object before the headers are sent to the client.
PreSendRequestContent Fired before the httpApplication object sends the content to the client.

Registering a Custom httpModule

Before a custom module can be used in an application, it must be registered in the site’s web.config file. A module registered in the GAC can also be registered in the machine.config file, but use caution when editing anything in machine.config because any changes affect all sites on the server.

The httpModules element is a child of the system.web element in the configuration file. The element can have any of three child elements:

  • add — Used to register httpModules with an application
  • clear — Removes all modules from an application
  • remove — Used to remove a specific module from an application

The add and remove elements have two required attributes used to configure the modules — name and type. The name attribute specifies the class name of the module, and the type attribute specifies the full name of the assembly, which can also contain the version, culture, and public key tokens. Here’s how to register custom modules in web.config:

<system.web>

<httpModules>

<add name="ASPPipeline" type="ASPPipeline"/>

<add name="ScriptModule" type="System.Web.Handlers.ScriptModule, System.Web.Extensions, Version=3.5.0.0, Culture=neutral, PublicKeyToken=31BF3856AD364E35"/>

</httpModules>

</system.web>

Notice the registration of the ScriptModule for the ASP.NET AJAX framework. It registers the module class name in the name attribute. It then registers the fully qualified name of the class library, the namespace of the class, version, culture, and public token key. This is a good example of a complete registration of a module contained in a class library. The more information that is provided in the module registration, the easier it is to have multiple versions registered in your site and in the GAC.

The order in which custom modules are registered is also important. Modules are executed in the same order in which they are registered in the configuration file. The standard modules registered in machine.config will be processed before any custom modules registered in web.config. You can remove individual machine.config modules or clear them all together and add them back to a site’s configuration meshed with any custom modules you create. This will give you the ultimate control over when processing occurs.

Default httpModules

There are several modules included in the default installation of ASP.NET, but not all of them may be needed by every application. They are registered in the default machine.config file installed with the ASP.NET framework. Unnecessary modules can be removed from the local web.config file. This takes them out of the processing pipeline, making the application perform better.

These modules manage everything from caching to authentication to error handling. Most of these modules should be used in every application, but some can be removed. If you are using only Forms Authentication, for example, the Windows and Passport authentication modules can be removed from the application. This gives the application a slight performance boost.

Here are the httpModules included in a clean install of ASP.NET in machine.config:

<httpModules>

            <add name="OutputCache" type="System.Web.Caching.OutputCacheModule"/>

            <add name="Session" type="System.Web.SessionState.SessionStateModule"/>

            <add name="WindowsAuthentication" image

type="System.Web.Security.WindowsAuthenticationModule"/>

            <add name="FormsAuthentication" image

type="System.Web.Security.FormsAuthenticationModule"/>

            <add name="PassportAuthentication" image

type="System.Web.Security.PassportAuthenticationModule"/>

            <add name="RoleManager" type="System.Web.Security.RoleManagerModule"/>

            <add name="UrlAuthorization" image

type="System.Web.Security.UrlAuthorizationModule"/>

            <add name="FileAuthorization" image

type="System.Web.Security.FileAuthorizationModule"/>

            <add name="AnonymousIdentification" image

type="System.Web.Security.AnonymousIdentificationModule"/>

            <add name="Profile" type="System.Web.Profile.ProfileModule"/>

            <add name="ErrorHandlerModule" image

type="System.Web.Mobile.ErrorHandlerModule, System.Web.Mobile, image

Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a"/>

            <add name="ServiceModel" image

type="System.ServiceModel.Activation.HttpModule, System.ServiceModel, image

Version=3.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089"/>

</httpModules>

Remove Registered Modules

Sometimes you may need to remove previously registered modules. This can be done in the httpModules element by including a child element, remove, that designates the name of the unwanted module, as the following examples show. Each web server registers several modules that may not be needed by a site, such as unused authentication modules.

<remove name="WindowsAuthentication" />

<remove name="PassportAuthentication" />

The clear element could also be used at the top of the httpModules section to clear all registered modules from being inherited by the site. Then you can add only the modules that will be used by the site after the clear element. Be careful that you do not remove a module that may be used by your site. If you get an exception regarding a module, examine your code or add a reference to the module.

Use a Custom Module to Configure the Site

Distribution of applications has long been a problem for any software project. That’s one of the reasons web sites have become a popular medium to build applications. But uploading or setting sites up on a web server has its own issues. Ensuring that folder structures, reference files, and class libraries are set up and configured correctly on the server can be quite a task. A custom module can be used to verify that an ASP.NET site has many of its dependencies in place, delay load assemblies, and pre-cache data or content.

httpModules are not limited to assigning event handlers to alter the request and the subsequent response to the user. Many modules actually initialize the web site. For example, the SessionStateModule uses the Init method to configure values from the configuration file concerning session management. This can be done the first time an application is loaded by executing a method from the Init method. Init can be thought of as a mechanism similar to the Page_Load event in a page. Any code executed from the Init method runs the first time a page or resource is requested through the ASP.NET engine. That’s why you use this method to register event handlers from the ASP.NET pipeline.

The following code checks for the existence of a folder structure under the site’s root for a vacation photo album. First, the path to the site’s root path is acquired and passed to a method that then ensures the needed folder structure.

VB.NET

Imports System.IO

Imports System.Web.Security

Imports Microsoft.VisualBasic

 

Public Class VerifyAppSetup

    Implements IHttpModule

 

 

    Public Sub Dispose() Implements System.Web.IHttpModule.Dispose

 

    End Sub

 

    Public Sub Init(ByVal context As System.Web.HttpApplication) Implements image

System.Web.IHttpModule.Init

 

        Dim sRoot As String = context.Context.Request.PhysicalApplicationPath

 

        CreateImageFolders(Path.Combine(sRoot, "Vacation"))

 

    End Sub

 

    Private Sub CreateImageFolders(ByVal Root As String)

 

        EnsureFolder(Path.Combine(Root, "Thumb"))

        EnsureFolder(Path.Combine(Root, "Display"))

        EnsureFolder(Path.Combine(Root, "Original"))

 

    End Sub

 

    Private Shared Sub EnsureFolder(ByVal sFolder As String)

        If Directory.Exists(sFolder) = False Then

            Directory.CreateDirectory(sFolder)

        End If

    End Sub

 

End Class

C#

using System;

using System.Text;

using System.Web;

using System.IO;

using System.Web.Security;

 

/// <summary>

/// Example httpModule to demonstrate how

/// they can be used to ensure an application's

/// configuration.

/// </summary>

/// <remarks></remarks>

public class VerifyAppSetup : IHttpModule

{

 

    public void Dispose()

    {

 

    }

 

    public void Init(System.Web.HttpApplication context)

    {

 

        string sRoot = context.Context.Request.PhysicalApplicationPath;

 

        CreateImageFolders(Path.Combine(sRoot, "Vacation"));

 

    }

 

    /// <summary>

    /// Creates the strings representing the Image

    /// folder structure for a photo album.

    /// </summary>

    /// <param name="Root"></param>

    /// <remarks></remarks>

    private void CreateImageFolders(string Root)

    {

 

        EnsureFolder(Path.Combine(Root, "Thumb"));

        EnsureFolder(Path.Combine(Root, "Display"));

        EnsureFolder(Path.Combine(Root, "Original"));

 

    }

 

    /// <summary>

    /// Checks for a folders existence,

    /// if it does not exist then the folder is created.

    /// </summary>

    /// <param name="sFolder"></param>

    /// <remarks></remarks>

    private static void EnsureFolder(string sFolder)

    {

        if (Directory.Exists(sFolder) == false)

        {

            Directory.CreateDirectory(sFolder);

        }

    }

 

}

The Init method sets a variable to the root of the web site, sRoot, that it uses to create a series of folders to store images for the vacation folder. Each photo will be stored in the original folder, where the actual file uploaded to the server is kept. The Display folder contains a copy of the file, resized to fit the desired image dimensions. One thing this module needs is the capability to write to the drive and also to create folders. Because security policies may not always allow ASP.NET sites to write to the file system, make sure the Web application has Write permission to the appropriate directories on the server. For a full production application, you might log to an exception log and redirect to a custom error message page to relay a message appropriately to the user.

Creating a Background Thread Process

How to create a background thread in an ASP.NET web site is an often-asked question. A background thread that continuously executes can be used to perform various monitoring and logging tasks, including synchronizing with third-party data sources or web services for updates and perhaps periodically creating a new site map file to account for changes to the site’s navigational structure.

The following example creates a new thread with a callback method (LogTime) that logs the time to a text file. LogTime runs a continuous loop that stores the current time every 15 seconds.

VB.NET

Imports Microsoft.VisualBasic

Imports System.web

Imports System.io

Imports System.Threading

 

Public Class BackGroundModule

    Implements IHttpModule

 

    Public Shared sRoot As String

Public t as Thread

 

    Public Sub Dispose() Implements System.Web.IHttpModule.Dispose

         t.abourt()

    End Sub

 

    Public Sub Init(ByVal context As System.Web.HttpApplication) Implements image

System.Web.IHttpModule.Init

        sRoot = context.Context.Request.PhysicalApplicationPath

        Dim t As New Thread(AddressOf LogTime)

        t.Start()

    End Sub

 

    Public Shared Sub LogTime()

 

        While true

            My.Computer.FileSystem.WriteAllText( _

                Path.Combine(sRoot, "background.log"), Now.ToString & vbCrLf, True)

            Thread.Sleep(15000)

        End While

 

    End Sub

 

End Class

C#

using Microsoft.VisualBasic;

using System.Web;

using System.IO;

using System;

using System.Threading;

 

public class BackGroundModule : IHttpModule

{

 

    public static string sRoot;

    public Thread t;

 

    public void Dispose()

    {

        t.Abort();

    }

 

    public void Init(System.Web.HttpApplication context)

    {

        sRoot = context.Context.Request.PhysicalApplicationPath;

        t = new Thread(LogTime);

        t.Start();

    }

 

    public static void LogTime()

    {

 

        while (true)

        {

            Stream str = File.Open(Path.Combine(sRoot, "background.log"),

                FileMode.Append, FileAccess.Write);

            StreamWriter stw = new StreamWriter(str);

            stw.WriteLine(DateTime.Now.ToLongTimeString() + " ");

 

            stw.Close();

            str.Close();

 

            Thread.Sleep(15000);

        }

 

    }

 

}

The class file contains additional namespace references to System.Threading and System.IO. These references make it easier to access members for threading and file system processing.

In the module’s Init method, a new thread is instantiated and started. The constructor of the new thread takes a reference to the callback method (LogTime in this example). The next line calls the thread’s Start method to kick it off. This invokes LogTime as part of that thread.

The LogTime method is a simple loop that appends the current time to a background.log file. The method then instructs the thread to sleep or pause for 15 seconds (15,000 milliseconds).

The thread runs in the background and does very little processing, so there is not a huge impact on the server. Background threads are very useful in performing background tasks and checking the state of the site or resources. Connecting to remote or third-party data sources is one of the primary uses, but the threads could also be leveraged to monitor the state of a web site.

Modify Rendered Content

One of the most basic actions for a custom module is to modify the content being sent to the browser. This can be done in any event where the Response object has been instantiated; the EndRequest event handler is a common event to inject content at the end of the stream. Because each event handler in a custom module accepts a reference to the httpApplication object, you can access the current httpContext object, which, in turn, gives you access to the Response object, or the content being sent to the browser.

As with most custom modules, an event handler needs to be registered in the Init method, this time for the EndRequest event. In the event handler, it is a good idea to cast local instances of the Application and Context objects. The following example shows how to inject the current time at the end of the text being sent to the browser.

VB.NET

Imports Microsoft.VisualBasic

Imports System.Text

Imports System.IO

 

Public Class ModifyContent

    Implements IHttpModule

 

    Public Sub Dispose() Implements System.Web.IHttpModule.Dispose

 

    End Sub

 

    Public Sub Init(ByVal context As System.Web.HttpApplication) Implements image

System.Web.IHttpModule.Init

        AddHandler context.ReleaseRequestState, AddressOf Me.Application_EndRequest

    End Sub

 

    Private Sub Application_EndRequest(ByVal source As Object, _

        ByVal e As EventArgs)

 

        Dim application As HttpApplication = _

            DirectCast(source,  HttpApplication)

        Dim context As HttpContext = application.Context

 

        If context.Response.BufferOutput = False Then

            context.Response.Write("<P>Content is Buffered, Not all elements image

can be optimized.</P>")

        End If

 

        context.Response.Filter = New OptimizeFilter()

 

        Select Case context.Response.ContentType.ToString

 

            Case "text/html"

                context.Response.Write( _

                    String.Format("Mime Type:{0}", image

context.Response.ContentType.ToString))

                context.Response.Write( _

                String.Format("<h3 align=""center"">{0}</h3>", "Welcome to image

WROX BLOX"))

 

        End Select

 

    End Sub

 

End Class

C#

using System;

using System.Web;

using System.Text;

using System.IO;

 

public class ModifyContent : IHttpModule

{

 

    public void Dispose()

    {

 

    }

 

    public void Init(System.Web.HttpApplication context)

    {

        context.ReleaseRequestState += this.Application_ReleaseRequestState;

    }

 

    private void Application_ReleaseRequestState(object source, EventArgs e)

    {

 

        HttpApplication application = (HttpApplication)source;

        HttpContext context = application.Context;

 

        if (context.Response.BufferOutput == false)

        {

            context.Response.Write("<P>Content is Buffered, Not all elements image

can be optimized.</P>");

        }

 

        context.Response.Filter = new OptimizeFilter();

 

        switch (context.Response.ContentType.ToString())

        {

 

            case "text/html":

                context.Response.Write(

                    String.Format("Mime Type:{0}", image

context.Response.ContentType.ToString()));

                context.Response.Write(

                    String.Format("<h3 align="center">{0}</h3>", "Welcome to image

WROX BLOX"));

                break;

 

        }

 

    }

 

}

Just like in a regular page, you can use the Response.Write method to pass text to the stream of text being sent to the browser. Because this is being done at the end of the stream, it will be shown at the bottom of the page. You could just as easily have added a handler for the AuthorizeRequest event (and placed content at the top of the page) or any of the events in the pipeline.

There’s also a check to see if the Response is being buffered. If the Response is not being buffered, the page or output may behave differently because the content being sent to the browser may be flushed to the client at any time in processing. That means that some of the page’s output may already be at the client as it is being built on the server. This technique is used by some pages to either keep them alive for long-running processes or increase perceived rendering speed on the client.

There is also a check to see if you are streaming text or HTML. This is done by checking the ContentType or Mime Type header, which tells the browser what type of content is being sent. This could be many things, including images, Word documents, PDFs, and zip files. Appending text to the end of one of the binary formats might cause some serious issues with the file on the client, or an exception, or it may be just ignored all together. The responsible thing to do is to verify the content being sent as text before injecting text into the stream.

Each response from the web site includes a header specifying the Mime Type. These headers specify the file type or instruct the browser so that it can properly open the file. Common Mime Types are text/html, text/xml, text/css, and text/javascript for web resources. Images, audio, video, and applications all have Mime Types defined to better instruct the client as to how to open the file. Vendors also have specific Mime Types for their applications, such as application/word for Microsoft Word, which is why the document will open MS Word instead of offering to be downloaded.

Optimizing the Response Output

One of the more advanced uses of a custom module is to completely modify the output stream before it is sent to the browser. You’ve already seen how to slip some text into a page before it is sent to the browser. The real power in this technique is intercepting the outgoing content and optimizing it. With an ASP.NET page, that could be as simple as injecting or replacing some content, or as complex as moving the ViewState object or JavaScript to the bottom of the page, or compressing the content. This work cannot be done directly in the module or the Response object; it needs to be done in a custom Stream class.

There are many ways to optimize the content being sent to the browser, and many ASP.NET developers are not aware of these techniques. A very popular trick is to move the ViewState object to the bottom of a page, so it is rendered late in the cycle on the client. While that can be done in a custom page class, it would be nice to push the process to a custom module so all ASP.NET pages will have this done and you can take it out of a custom page class.

The following example demonstrates the use of a custom Stream class that examines the text being written out and pulls the ViewState as well as all other Hidden Input elements from the top of the page and then injects them at the bottom of the HTML before it is sent to the browser. The custom stream has two private members: mFilter (a Stream object) and mEncoding (a System.Text.Encoding object). These values are set to match the Filter and ContentEncoding properties of the current Response object in the class’s constructor.

VB

Public Class OptimizeFilter

    Inherits IO.Stream

 

    Private mFilter As IO.Stream

    Private mEncoding As System.Text.Encoding

 

    Sub New()

 

        'If there is not a current Context object,

        'we have nothing to work with.

        If Not IsNothing(HttpContext.Current) Then

            Dim Response As HttpResponse = HttpContext.Current.Response

 

            mFilter = Response.Filter

            mEncoding = Response.ContentEncoding

        End If

 

    End Sub

     End Class

C#

public class OptimizeFilter : System.IO.Stream

{

 

    private System.IO.Stream mFilter;

    private System.Text.Encoding mEncoding;

 

    public OptimizeFilter()

    {

 

        //If there is not a current Context object,

        //we have nothing to work with.

        if ((HttpContext.Current != null))

        {

            HttpResponse Response = HttpContext.Current.Response;

 

            mFilter = Response.Filter;

            mEncoding = Response.ContentEncoding;

        }

 

    }

  }

The class contains several Public ReadOnly properties to return the underlying values of the Response’s Filter, such as CanRead, CanWrite, and Length. These properties keep the custom Stream consistent with the real Response object’s stream:

VB

Public Overrides ReadOnly Property CanRead() As Boolean

    Get

        Return mFilter.CanRead

    End Get

End Property

 

Public Overrides ReadOnly Property CanSeek() As Boolean

    Get

        Return mFilter.CanSeek

    End Get

End Property

 

Public Overrides ReadOnly Property CanWrite() As Boolean

    Get

        Return mFilter.CanWrite

    End Get

End Property

 

Public Overrides Sub Flush()

    Call mFilter.Flush()

End Sub

 

Public Overrides ReadOnly Property Length() As Long

    Get

        Return mFilter.Length

    End Get

End Property

 

Public Overrides Property Position() As Long

    Get

        Return mFilter.Position

    End Get

    Set(ByVal Value As Long)

        mFilter.Position = Value

    End Set

End Property

 

Public Overrides Function Read(ByVal buffer() As Byte, ByVal offset As image

Integer, ByVal count As Integer) As Integer

 

    If mFilter.CanRead Then

        Return mFilter.Read(buffer, offset, count)

    Else

         Return -1

    End If

 

End Function

 

Public Overrides Function Seek(ByVal offset As Long, ByVal origin As image

System.IO.SeekOrigin) As Long

 

    If mFilter.CanSeek Then

        Return mFilter.Seek(offset, origin)

    Else

        Return -1

    End If

 

End Function

          

Public Overrides Sub SetLength(ByVal value As Long)

 

    Call mFilter.SetLength(value)

 

End Sub

      C#

    public override bool CanRead

    {

        get { return mFilter.CanRead; }

    }

 

    public override bool CanSeek

    {

        get { return mFilter.CanSeek; }

    }

 

    public override bool CanWrite

    {

        get { return mFilter.CanWrite; }

    }

 

    public override void Flush()

    {

        mFilter.Flush();

    }

 

    public override long Length

    {

        get { return mFilter.Length; }

    }

 

    public override long Position

    {

        get { return mFilter.Position; }

        set { mFilter.Position = value; }

    }

 

    public override int Read(byte[] buffer, int offset, int count)

    {

 

        if (mFilter.CanRead)

        {

            return mFilter.Read(buffer, offset, count);

        }

        else

        {

            return -1;

        }

 

    }

 

    public override long Seek(long offset, System.IO.SeekOrigin origin)

    {

 

        if (mFilter.CanSeek)

        {

            return mFilter.Seek(offset, origin);

        }

        else

        {

            return -1;

        }

 

    }

 

    public override void SetLength(long value)

    {

 

        mFilter.SetLength(value);

 

    }

Finally the custom Stream class overrides the Write method of the Stream. The code to move the Hidden Input tags resides in the Write method. This routine uses a StringBuilder to get the underlying text being sent to the browser and sets it to a string that can be used to parse and update.

Moving the ViewState object to the bottom of the page is a relatively easy thing to do, and there are several implementations on the Web right now. I like to use Regular Expressions to find and extract the ViewState because they tend to be much more concise and efficient at string manipulations than creating complicated string manipulation routines. This can be done by running the regular expression on the text being sent to the browser and extracting the ViewState element and placing it at the bottom:

VB

    Public Overrides Sub Write(ByVal buffer() As Byte, ByVal offset As image

Integer, ByVal count As Integer)

 

        ' Place Response Text In A String Builder and then set the contents

        ' to a string.

        Dim sbhtml As New StringBuilder(mEncoding.GetString(buffer, offset, count))

        Dim html As String = sbhtml.ToString

 

        'Regex Expression to extract the hidden input elements.

        Dim sPattern As String = "<input type=""hidden"".*/*>"

        Dim mc As MatchCollection = Regex.Matches(html, sPattern, _

            RegexOptions.IgnoreCase & RegexOptions.IgnorePatternWhitespace)

        Dim sb As New StringBuilder

 

        For Each m As Match In mc

 

            sb.AppendLine(m.Value)

            html = html.Replace(m.Value, String.Empty)

 

        Next

 

        'Place the extracted elements in front of the closing form tag.

        'Then remove any empty DIV tags.

        Dim output As String = _

            Regex.Replace(html.Replace("</form>", sb.ToString & "</form>"), image

"<div>s*</div>", "")

 

        ' Write The Newly Modified Response Stream

        mFilter.Write(mEncoding.GetBytes(output.ToString), 0, output.Length)

 

    End Sub

C#

    public override void Write(byte[] buffer, int offset, int count)

    {

 

        if ((mFilter == null))

        {

            return; // TODO: might not be correct. Was : Exit Sub

        }

 

        // Place Response Text In A String Builder and then set the contents image

to a string.

        StringBuilder sbhtml = new StringBuilder(mEncoding.GetString(buffer, image

offset, count));

        string html = sbhtml.ToString();

 

        //Regex Expression to extract the hidden input elements.

        string sPattern = "<input type="hidden".*/*>";

        MatchCollection mc =

            Regex.Matches(html, sPattern,

            RegexOptions.IgnoreCase & RegexOptions.IgnorePatternWhitespace);

        StringBuilder sb = new StringBuilder();

 

        foreach (Match m in mc)

        {

 

            sb.AppendLine(m.Value);

            html = html.Replace(m.Value, string.Empty);

 

        }

 

        //Place the extracted elements in front of the closing form tag.

        //Then remove any empty DIV tags.

        string output = Regex.Replace(html.Replace("</form>", sb.ToString() image

+ "</form>"), "<div>\s*</div>", "");

 

        // Write The Newly Modified Response Stream

        mFilter.Write(mEncoding.GetBytes(output.ToString()), 0, output.Length);

 

    }

Regular Expressions are a language all to themselves, and they cross platform and language boundaries. They are used to parse strings for patterns and are often used to format, validate, and replace text based on the specified pattern. The RegEx and associated classes in the .NET Framework provide a rich set of tools for the .NET developer to leverage Regular Expressions.

The actual httpModule class registers event handlers for both the ReleaseRequestState and PreSendRequestHeaders events. Both events are handled to ensure that you access the content at the last possible moment to make the modifications before it is sent to the browser. Both events are handled by the same method, MoveViewState.

MoveViewState creates local instances of the httpApplication and the current httpContext. It then checks to see that page buffering is enabled; if not, then it exits the method because it becomes much harder to manage moving content for an unbuffered page.

In ASP.NET, pages can be buffered or are stored on the server as content is being added to the eventual output. If buffering is not turned on, moving items such as the ViewState is dramatically harder. Unbuffered pages send content to the client in small chunks, with the idea being, it is better for the visitor to see something, rather than wait for a full page to render. However, that isn’t always helpful for performance because multiple packets of data mean more overhead than just a single packet of data.

Finally, the method checks to ensure that you are sending HTML content to the client by evaluating the Mime Type. You could check for the page extension, but for demonstration purposes, let’s rely on the Mime Type. Plus, the file extension is not always a valid indicator as to the actual httpHandler being used to process the request. MySpace.com, for instance, retains the .cfm extensions for the ColdFusion with which it was originally built. The site has been an ASP.NET application for a long time; the extensions are retained for backward compatibility

The final line of the MoveViewStateModule assigns an instance of the OptimizeFilter to the Response object’s filter, which is a stream that contains the content being sent to the browser. You cannot access this information directly, but you can replace it with a custom stream. By overriding the default Stream, you make an instance of the OptimizeFilter, the filter used by the Response object to send content to the browser. Thus the Write method that is overridden in the OptimizeFilter class is used instead of the normal routine, allowing you the capability to change the content being sent to the browser at the last instant.

Create an IP Filter

Hackers tend to use multiple zombie computers to execute their attacks. That is why there are several blacklists used by e-mail administrators to block SPAM. Web sites are targets for hackers and even SPAMMERS trying to break into the sites for malicious or just plain annoying reasons. The attackers often will use a group of zombie computers or proxy servers to launch their attacks. Each will have an IP address that can be used to track the activity’s source.

Hackers try to break into the site for malicious purposes, which could be as simple as placing bogus links on the site or an all-out assault. Many of these attacks follow a specific pattern, and you can track those patterns based on URLs, posted variables, and so on. This is much the way the URLScan utility works. If you have a library of common attack patterns or trap common exceptions, you can create an httpModule to build an IP blacklist as well as check each incoming request to see if it is from a blocked IP address.

Although firewalls and even the operating system can block lists of unwanted IP addresses, web-site developers are often not given the proper access to them by the IT staff. A custom httpModule can read the list of banned IP addresses and prohibit access by those addresses directly in the web site. To do that, you need to add an event handler — CheckRequest in the following example — for the AuthorizeRequest event. This handler will call another method, CheckIP, to see if the IP is banned. If the IP address is banned, CheckIP uses URL rewriting to change the request to a banned message page. If a request is from a good IP address, CheckIP allows it to proceed unblocked.

VB.NET

Imports Microsoft.VisualBasic

 

Public Class IpBlocker

    Implements IHttpModule

 

    Private _Context As HttpApplication

 

    Public Sub Dispose() Implements System.Web.IHttpModule.Dispose

' Because this Module has a class-level variable holding the HttpApplication,

' you should remove the Event Handler.

RemoveHandler _Context.AuthorizeRequest, AddressOf CheckRequest

    End Sub

 

    Public Sub Init(ByVal context As System.Web.HttpApplication) Implements image

System.Web.IHttpModule.Init

        _Context = context

 

        AddHandler context.AuthorizeRequest, AddressOf CheckRequest

 

    End Sub

 

    Sub CheckRequest(ByVal sender As Object, ByVal e As EventArgs)

 

        If CheckIP() Then

            HttpContext.Current.RewritePath("~/Banned.htm")

        End If

 

    End Sub

 

    Public Function CheckIP() As Boolean

 

        Dim fileContents As String = My.Computer.FileSystem.ReadAllText( _

        HttpContext.Current.Server.MapPath("") & "adIps.dat")

 

        'OK I am going to cheat for demonstration purposes and add an extra

        'check against a querystring parameter for an IP address

        Dim sIP As String = String.Empty

 

        If Not IsNothing(HttpContext.Current.Request.QueryString("IPcheck")) Then

            sIP = HttpContext.Current.Request.QueryString("IPcheck").ToString

        Else

            sIP = HttpContext.Current.Request.UserHostAddress.ToString

        End If

 

        If fileContents.Contains(sIP)  Then

            Return True

        Else

            Return False

        End If

 

    End Function

 

End Class

C#

using System.IO;

using System.Web;

using System;

using System.Text;

 

public class IpBlocker : IHttpModule

{

 

    private static HttpApplication _Context;

    public static string sRoot;

 

 

    public void Dispose()

    {

// Because this Module has a class-level variable holding the HttpApplication,

// you should remove the Event Handler.

        _Context.AuthorizeRequest -= CheckRequest;

    }

 

    public void Init(System.Web.HttpApplication context)

    {

        _Context = context;

        sRoot = context.Context.Request.PhysicalApplicationPath;

 

        context.AuthorizeRequest += CheckRequest;

 

    }

 

    public void CheckRequest(object sender, EventArgs e)

    {

 

        if (CheckIP())

        {

            HttpContext.Current.RewritePath("~/Banned.htm");

        }

 

    }

 

    public bool CheckIP()

    {

 

        Stream str = File.Open(Path.Combine(sRoot, "badIps.dat"),

            FileMode.OpenOrCreate , FileAccess.Read);

        StreamReader stw = new StreamReader(str);

        string fileContents = stw.ReadToEnd();

        stw.Close();

        str.Close();

 

        //OK I am going to cheat for demonstration purposes and add an extra

        //check against a querystring parameter for an IP address

        string sIP = string.Empty;

 

        if ((_Context.Request.QueryString["IPcheck"] != null))

        {

            sIP = _Context.Request.QueryString["IPcheck"].ToString();

        }

        else

        {

            sIP = _Context.Request.UserHostAddress.ToString();

        }

 

        if (fileContents.Contains(sIP))

        {

            return true;

        }

        else

        {

            return false;

        }

 

    }

 

}

Of course, this is an oversimplified example of building an IP blacklist. A real production IP blocker would have a sophisticated selection retaining and checking algorithm. This example uses a simple text file to compare against.

For testing purposes, there is a backdoor to see if the routine works; an IP address can be passed in the QueryString, HttpContext.Current.Request.QueryString("IPcheck"). This gives you the capability in a small test environment to see if the module performs proper blocking.

Rewrite a Request

A popular technique among many Web developers is to use URL rewriting to provide more friendly URLs. The problem arises when using the QueryString to pass parameters to the page to generate dynamic content. This is not a search engine or user-friendly way to structure the URL, and it is not easy for visitors to the site to remember the URL.

To effectively execute URL rewriting, it needs to be done either as an ISAPI filter or as a custom module. The module should intercept the authenticate or authorize request and examine the requested URL to see if it should be modified on the server. These events are raised before any server-side processing of the request begins, so you can still change the actual target resource to be processed.

The secret to executing URL rewriting is passing the actual URL to be processed to the RewritePath method. The real magic is determining how to remap URLs. Commonly this is done either with a series of regular expressions, a database table, or a simple routine that maps URLs.

The following example shows how to do a simple URL rewriting to set the background color of the page color.aspx. But a user would request {color}.aspx, and the color would be translated to a QueryString parameter that is be used to dynamically change the style for the background color of the page. A request for red.aspx, for instance, would actually be translated to color.aspx?color=red. This example uses a simple Select Case statement to handle the mapping.

It also uses the AuthorizeRequest event as the hook to execute the rewriting. This event fires before any actual processing of the request, thus changing the resource being requested can still be done.

VB.NET

Imports Microsoft.VisualBasic

Imports System.IO

 

Public Class URLRewrite

    Implements IHttpModule

 

 

    Public Sub Dispose() Implements System.Web.IHttpModule.Dispose

 

    End Sub

 

    Public Sub Init(ByVal context As System.Web.HttpApplication) Implements image

System.Web.IHttpModule.Init

        AddHandler context.AuthorizeRequest, AddressOf AuthorizeRequest

    End Sub

 

    Private Sub AuthorizeRequest(ByVal sender As Object, ByVal e As EventArgs)

        Dim app As HttpApplication = CType(sender, HttpApplication)

        Rewrite(app.Request.Path, app)

    End Sub

 

    Private Sub Rewrite(ByVal requestedPath As String, ByVal app As image

HttpApplication)

 

        Select Case Path.GetFileName(app.Context.Request.RawUrl)

            Case "red.aspx"

                HttpContext.Current.RewritePath("~/color.aspx?color=red")

            Case "green.aspx"

                HttpContext.Current.RewritePath("~/color.aspx?color=green")

            Case "blue.aspx"

                HttpContext.Current.RewritePath("~/color.aspx?color=blue")

            Case "black.aspx"

                HttpContext.Current.RewritePath("~/color.aspx?color=black")

            Case "gray.aspx"

                HttpContext.Current.RewritePath("~/color.aspx?color=gray")

 

                'If you do not have one of these pages being requested, then

                'just let it pass through.

 

        End Select

 

    End Sub

 

End Class

C#

using System;

using System.IO;

using System.Web;

 

public class URLRewrite : IHttpModule

{

 

 

    public void Dispose()

    {

 

    }

 

    public void Init(System.Web.HttpApplication context)

    {

        context.AuthorizeRequest += AuthorizeRequest;

    }

 

    private void AuthorizeRequest(object sender, EventArgs e)

    {

        HttpApplication app = (HttpApplication)sender;

        Rewrite(app.Request.Path, app);

    }

 

    private void Rewrite(string requestedPath, HttpApplication app)

    {

 

        switch (Path.GetFileName(app.Context.Request.RawUrl))

        {

            case "red.aspx":

                HttpContext.Current.RewritePath("~/color.aspx?color=red");

                break;

            case "green.aspx":

                HttpContext.Current.RewritePath("~/color.aspx?color=green");

                break;

            case "blue.aspx":

                HttpContext.Current.RewritePath("~/color.aspx?color=blue");

                break;

            case "black.aspx":

                HttpContext.Current.RewritePath("~/color.aspx?color=black");

                break;

            case "gray.aspx":

                HttpContext.Current.RewritePath("~/color.aspx?color=gray");

                break;

 

            //If you do not have one of these pages being requested, then just

            //let it pass through.

 

        }

 

    }

 

}

Opening any of the specified color pages now opens a page that sets the background color. If the page is not handled in the Select Case statement, it falls through to normal processing. If the page does not actually exist, then a 404 or “not found” status code will be sent to the browser. This demonstrates how a module does not necessarily affect every request, if there are checks made to ensure it meets certain criteria.

It is always a good idea to have a customer’s 404 set up in an ASP.NET at the web site. This can simply be done by configuring the custom error section and the web.config file. You can actually specify what pages will be loaded based on different HTTP status codes. You could also use your URL rewriting engine to reroute requests for nonexistent pages based on the actual request.

A Custom Error Handler

It is always a good idea to handle any errors that may occur in an application where they might happen. In ASP.NET, you can catch an exception at the point it occurs, at the page level, or at the application level. Exceptions that make it to the application level can be handled in the Global.asax or in an httpModule. Custom errors can also be mapped to a special page for visitors to view; this does not perform any processing to clear the error, but should be turned on for any production site.

The Error event will always fire when an unhandled exception bubbles up, even when custom errors are turned on in a web site. By employing a custom error module to handle the Error event, you can log the exception information to any data store or multiple data stores. Here’s an example custom error logging module:

VB.NET

Imports System.Net.Mail

Imports System.IO

Imports Microsoft.VisualBasic

 

Public Class CustomErrorModule

    Implements IHttpModule

 

    Public Sub Dispose() Implements System.Web.IHttpModule.Dispose

 

    End Sub

 

    Public Sub Init(ByVal context As System.Web.HttpApplication) Implements image

System.Web.IHttpModule.Init

        AddHandler context.Error, AddressOf ErrorHandler

    End Sub

 

    Public Sub ErrorHandler(ByVal sender As Object, ByVal e As EventArgs)

 

        Dim request As HttpRequest = DirectCast(sender, HttpApplication).Request

        Dim context As HttpContext = DirectCast(sender, HttpApplication).Context

 

        Try

 

            'Create a StringBuilder because they are more efficient for

            'concatination operations.

            Dim sbError As New StringBuilder

 

            sbError.AppendFormat("URL - {0}", request.Url.ToString)

            sbError.AppendFormat("Browser - {0}", request.Browser.Browser.ToString)

            sbError.AppendFormat("Browser Version - {0}", request.Browser.Version)

 

            If Not IsNothing(request.UrlReferrer) Then

                sbError.AppendFormat("UrlReferrer - {0}", image

request.UrlReferrer.ToString)

            End If

 

            sbError.AppendFormat("UserHostAddress - {0}", image

request.UserHostAddress.ToString)

            sbError.AppendFormat("UserHostName - {0}", image

request.UserHostName.ToString)

            sbError.AppendFormat("Last Error - {0}", context.Error.ToString)

            sbError.AppendFormat("Stack Trace - {0}", image

context.Error.StackTrace.ToString)

            sbError.AppendFormat("Error Source - {0}", image

context.Error.Source.ToString)

            sbError.AppendFormat("Error Message - {0}", image

context.Error.Message.ToString)

 

            SendError("Error in the httpModule Demo Site", sbError.ToString)

 

        Catch exception2 As Exception

            'Do this in case there is a problem sending or logging the exception

            If Not IsNothing(context) Then

                context.Response.Write(exception2.ToString)

                context.Server.ClearError()

            End If

            Return

 

        End Try

 

    End Sub

 

    Private Sub SendError(ByVal sSubject As String, _

            ByVal sMsg As String)

 

        ' For a real application the e-mail addresses, server, etc. should be

        ' stored in the config file.

        Try

 

            'This requires your SMTP configuration to be setup in the

            'web.config file.

            Dim smtp As New System.Net.Mail.SmtpClient()

            smtp.Send(New MailMessage("[email protected]", _

                "[email protected]", sSubject, sMsg))

 

        Catch exc As Exception

 

            'Do Something here as a backup logging mechanism

 

        Finally

 

        End Try

 

 

    End Sub

 

End Class

C#

using System.Net.Mail;

using System.IO;

using System.Web;

using System;

using System.Text;

 

 

public class CustomErrorModule : IHttpModule

{

 

    public void Dispose()

    {

 

    }

 

    public void Init(System.Web.HttpApplication Context)

    {

        Context.Error += ErrorHandler;

    }

 

    public void ErrorHandler(object sender, EventArgs e)

    {

 

        HttpRequest Request = ((HttpApplication)sender).Request;

        HttpContext Context = ((HttpApplication)sender).Context;

 

        try

        {

 

            //Create a StringBuilder because they are more efficient for

            //concatination operations.

            StringBuilder sbError = new StringBuilder();

 

            sbError.AppendFormat("URL - {0}", Request.Url.ToString());

            sbError.AppendFormat("Browser - {0}", image

Request.Browser.Browser.ToString());

            sbError.AppendFormat("Browser Version - {0}", Request.Browser.Version);

 

            if ((Request.UrlReferrer != null))

            {

                sbError.AppendFormat("UrlReferrer - {0}", image

Request.UrlReferrer.ToString());

            }

 

            sbError.AppendFormat("UserHostAddress - {0}", image

Request.UserHostAddress.ToString());

            sbError.AppendFormat("UserHostName - {0}", image

Request.UserHostName.ToString());

            sbError.AppendFormat("Last Error - {0}", Context.Error.ToString());

            sbError.AppendFormat("Stack Trace - {0}", image

Context.Error.StackTrace.ToString());

            sbError.AppendFormat("Error Source - {0}", image

Context.Error.Source.ToString());

            sbError.AppendFormat("Error Message - {0}", image

Context.Error.Message.ToString());

 

            SendError("Error in the httpModule Demo Site", sbError.ToString());

        }

 

        catch (Exception exception2)

        {

            //Do this in case there is a problem sending or logging the exception

            if ((Context != null))

            {

                Context.Response.Write(exception2.ToString());

                Context.Server.ClearError();

            }

            return;

 

        }

 

    }

 

    private void SendError(string sSubject, string sMsg)

    {

 

        //For a real application the e-mail addresses, server, etc. should be

        //stored in the config file.

        try

        {

 

            //This requires your SMTP configuration to be setup in the

            //web.config file.

            System.Net.Mail.SmtpClient smtp = new System.Net.Mail.SmtpClient();

            smtp.Send(new MailMessage("[email protected]", image

"[email protected]", sSubject, sMsg));

        }

 

        catch (Exception exc)

        {

        }

 

        //Do Something here as a backup logging mechanism

 

        finally

        {

 

        }

 

 

    }

 

}

In this example, the error is sent to a central e-mail address to notify the system that there’s a problem with the site. Of course, you could log it to a text file, the system error log, or just about any other place. If custom error handling is in place on the site, it will handle displaying the proper page to the user.

A StringBuilder is used to compile a list of information that should be helpful in tracking the bug and correcting it. It is built using information from the exception as well as from the Request object. Knowing information about the client’s request, such as information about the client’s browser and what URL was requested, makes it easier to troubleshoot. This is why creating a custom exception handler to log the information is important.

Custom Modules in IIS 7

IIS 7 is completely rewritten, and, in addition to having a new management interface, it also allows for custom modules to be installed into the web server either in native or managed mode. The capability to extend IIS can be done in either native C/C++ or managed code (C# or VB.NET, e.g.) in ASP.NET.

With IIS 7, you can create custom HTTP modules and install them on the web server itself — you aren’t limited just to the web site. To create a class library that can be installed into IIS, it must be strongly named and installed in the GAC.

Installing a custom module in IIS is a simple process. First, launch the IIS Site Manager by selecting Control Panel Administrative Tools Internet Information Services (IIS) Manager (see Figure 2).

In the IIS Manager, select the Modules item (see Figure 3).

For a .NET class library, click the Add Managed Module link at the top right corner of the window (see Figure 4).

In the Name field, enter a friendly name for the module. In the Type dropdown, select the module registered in the GAC. Finally, if you want this module to only process requests for ASP.NET resources, check the Invoke only for requests to ASP.NET applications or managed handlers option (see Figure 5).

Now your custom module will handle all requests made to the web server, not just to an individual site. This works great for servers that run multiple sites when you need to run a common module between all sites on the server. It is good for a consistent framework for all your corporate sites, or as a nice feature in a shared hosting environment.

Summary

Custom ASP.NET modules are a great way to process each and every request to a web site or to all sites on a server. They can be used to alter content before it is sent to the browser, redirect requests to different resources, set up applications, and even protect content from predators. Leveraging custom httpModules is a great way to enhance your application for advanced features in ASP.NET.

About Chris Love

Chris Love has more than 14 years of experience in software design, development, and architecture. He has been the principal developer for more than 250 small and medium ASP and ASP.NET web sites over the last 7 years. These projects have exposed Chris to a wide range of Microsoft-related technologies to solve real business problems for his clients. He has learned to look objectively at his clients’ businesses and offer pragmatic suggestions to make them more efficient and profitable.

He has focused primarily on ASP.NET and VB.NET to produce the majority of his web sites. SQL Server is his database of choice, but he has valuable experience with Microsoft Access and interfacing to 3270 Main Frames as well. Other responsibilities have included managing and maintaining production Windows servers for web, database, and e-mail. He manages performance, security, and SPAM safeguards to keep his customers operating 24/7.

Chris’s clients have also relied on his experience and expertise to help develop online marketing strategies, including search engine optimization and pay-per-click campaigns. Chris has begun to leverage this experience along with his ASP.NET expertise to build his own Web properties and practice his technical and marketing theories first-hand.

Chris has been active in a leadership role in a local user’s group, TRINUG, for more than 5 years. He served as the vice president for the first 3 years and is currently on the advisory board. He has also led monthly a Special Interest Group (SIG), where he covered practical ASP.NET programming techniques, including how to use httpHandlers, httpModules, extending the .NET framework, and using third-party tools. He frequently presents and organizes Code Camps around the country. He has recently completed his first book, ASP.NET 2.0: Your Visual Blueprint for Developing Web Applications.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset