Http Modules

The .NET framework provides several mechanisms for extending the behaviors of web pages and other endpoints. One such technique is the http module. Modules are often either overlooked as a solution or are considered too heavyweight or scary for use. This is unfortunate as they are a powerful item in the ASP.NET development toolbox.

An http module is a single unit of code that is executed as part of the web request pipeline. When a web request is received, the list of registered modules are afforded the opportunity to inject themselves at several stages in the execution pipeline. Each module has the opportunity to inspect or modify parts of the request before or after it is processed. This makes them a powerful mechanism for attaching custom behaviors. Modules also have a flexible activation model as they can be attached or detached via configuration allowing them to be enabled or disabled at will without affecting a production site.

The full source code for this example can be found here.

How to Create an HttpModule
The ASP.NET Module type is in the Web category under the Add New Item dialog.

241@229_tmp6C6D

Remove the default sample code that is included in the template. The LogContext event handler is implemented in the template for demonstration purposes but is not used in this example. Your class should now look similar to the following:

using System;
using System.Web;

namespace ModuleWebApp
{

    /// 
    /// Custom http module.
    /// 
    public class MyNewModule : IHttpModule
    {
        
        #region IHttpModule Members

        public void Dispose() {}

        /// 
        /// Initializes the module.
        /// 
        /// <param name="context">The application context.</param>
        public void Init(HttpApplication context)
        {
            
        }

        #endregion

    }
}

Initializing the Module

Modules perform work by registering handlers for events in the request execution pipeline such as BeginRequest and EndRequest. It is within the event handlers that they perform their task. In the example below, the Init method is used to register handlers for the BeginRequest and EndRequest events.

/// 
/// Raised when the module is initialized.
/// 
/// <param name="context">The application context.</param>
public void Init(HttpApplication context)
{
    context.BeginRequest += new EventHandler(context_BeginRequest);
    context.EndRequest += new EventHandler(context_EndRequest);
}

Implementing the Event Handlers and Finishing the Module

Next provide implementations for the event handlers. This example module is going to calculate and display the time it takes to execute a page within the application. The code example below is for the completed module class. It contains the following changes:

  • Declaration of a dictionary of stopwatches that stores the execution timers.
  • Implementation of the BeginRequest and EndRequest handlers to start and stop the timers.
  • Instantiation of the dictionary of timers in the Init method.
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Web;

namespace ModuleWebApp
{

    /// <summary>
    /// A custom http module used to time request execution.
    /// </summary>
    public class RequestTimer : IHttpModule
    {
        
        #region IHttpModule Members

        public void Dispose() { }

        /// <summary>
        /// Raised when the module is initialized.
        /// </summary>
        /// <param name="context">The application context.</param>
        public void Init(HttpApplication context)
        {
            _timers = new Dictionary<Guid, Stopwatch>();

            context.BeginRequest += new EventHandler(context_BeginRequest);
            context.EndRequest += new EventHandler(context_EndRequest);
        }

        #endregion


        #region Event Handlers

        /// <summary>
        /// Raised when the request begins.
        /// </summary>
        /// <param name="sender">The source of the event; the <see cref="System.Web.HttpApplication" />.</param>
        /// <param name="e">An <see cref="System.EventsArgs"/> that contains event data.</param>
        protected void context_BeginRequest(object sender, EventArgs e)
        {
            Guid timerId = Guid.NewGuid();
            Stopwatch timer = new Stopwatch();
            _timers.Add(timerId, timer);

            HttpContext.Current.Items.Add(EXECUTION_TIMER_KEY, timerId.ToString());

            timer.Start();
        }

        /// <summary>
        /// Raised when the request ends.
        /// </summary>
        /// <param name="sender">The source of the event; the <see cref="System.Web.HttpApplication" />.</param>
        /// <param name="e">An <see cref="System.EventsArgs"/> that contains event data.</param>
        protected void context_EndRequest(object sender, EventArgs e)
        {
            string timerId = HttpContext.Current.Items[EXECUTION_TIMER_KEY] as string;
            
            if (String.IsNullOrEmpty(timerId))
                return;

            Stopwatch timer = _timers[new Guid(timerId)];
            if (timer == null)
                return;

            timer.Stop();

            double elapsedSeconds = (timer.ElapsedMilliseconds) / (double)1000;

            HttpContext.Current.Response.Write(
                String.Format(
                    "Request executed in {0} seconds ({1} ticks).",
                    elapsedSeconds.ToString(), 
                    timer.ElapsedTicks.ToString()));
        }

        #endregion


        #region Fields

        /// <summary>
        /// The collection of execution timers.
        /// </summary>
        private Dictionary<Guid, Stopwatch> _timers;

        #endregion


        #region Constants

        private const string EXECUTION_TIMER_KEY = "__ExecutionTimer_Id";

        #endregion

    }

}

Some of the concepts used here are beyond the scope of this discussion. Research the Stopwatch class for more information on how to use timers. See the HttpContext class to learn more about request and response objects.

Registering the Module

The final step is to register the module so that it is instantiated and executed on every request. This is done via the web.config file. The <system.web> section contains an <httpModules> section. The registration configuration line for the module should be added to the <httpModules> section as shown below:


	
	
		
		
    
	

That’s it! Running the web application should display the default.aspx page, which now shows the execution time in elapsed seconds (and ticks). This information is displayed at the end of every web request so it works on every page in the system. If you inspect the context_EndRequest handler you can see the following lines:

	
HttpContext.Current.Response.Write(
	String.Format(
		"Request executed in {0} seconds ({1} ticks).",
		elapsedSeconds.ToString(), 
		timer.ElapsedTicks.ToString()));

The HttpContext class exposes the current Request and Response. These are the http request and response that are received from and sent to the client respectively. The execution time was displayed on the page because this handler wrote to the response object after the request had finished executing. The timer was started when the request started and stopped when the request ended. The time taken for the page to performing its processing and the request to finish is then displayed in the response. It is in this way that modules interact with all requests in the system and provide global level behaviors that are attached via configuration.

This entry was posted in .NET Framework, ASP.NET, Programming, Uncategorized and tagged , , . Bookmark the permalink.

Leave a Reply

Your email address will not be published. Required fields are marked *