Implementing HTTP Handlers in ASP.NET

Creating the Handler

One of the most important things to implementing your HTTP handler is the management of your URL mappings. Before you look at how the handler should be coded, you should put some thought into how flexible you want the mappings to be. There are countless methods for managing your mappings, each with its own set of pros and cons. For instance, you could technically put them in a database; which would allow you to setup a nice front-end to manage them from within your application. The problem with this is that you'll require a database call simply to find out what page you want to access. This may or may not be adequate. I would assume that the latter would be true in most situations. You should also consider the fact that, in some cases, you may require more than one rewrite or redirect in order to setup your mappings appropriately. For this article, I will keep it very simplistic. We will use the custom app settings section available within the Web.config file. To do this, add the following section to your Web.config file:

Web.config appSettings Configuration

< appSettings >
  <add key="/MyApp/LogicalPage1.aspx" value="~/Pages/PhysicalPage1.aspx" />
  <add key="/MyApp/LogicalPage2.aspx" value="~/Pages/PhysicalPage2.aspx" />
</appSettings>

The key is intended to be the requested page and the value is the physical page that will be displayed. Pretty simple. Two important things to note are that, using this simplified scenario, the keymust be and the value should be root-relative paths.. For instance, the above specifies that http://localhost/MyApp/LogicalPage1.aspx will actually map to http://localhost/MyApp/Pages/PhysicalPage1.aspx.

Now that we've defined our mappings, I recommend that you create a configuration settings reader to load and act upon the appropriate mapping at runtime. For this example, I won't get into that, though. This simple implementation only requires a one-line lookup, so there is not much of a need to have the settings reader; however, in a real-world app, I would highly suggest using one for extensibility reasons. I will discuss this more in-depth later.

Creating the HTTP Handler

Now that we have decided on our mapping storage method and have ensured a way to read the mappings (built-in configuration support for now), all we have to do is create the HTTP handler. There are a lot of different ways to do this, so the first thing to think about is: What do you want to do? For this article, we're just rewriting the URL, but for your system, you might want to add application-level logic. If this is the case, I recommend that you create special business objects to handle each logical task that needs to be accomplished. For instance, a LogAction class for logging or a RewriteUrl class for the URL rewriting. Since we will only be implementing a simple URL rewrite, I won't bother getting into the complexities of a separate class.

Before you set forth with creating your HTTP handler, you should take a look at the IHttpHandler interface, which you will need to implement.

IHttpHandler Interface

public interface IHttpHandler
{
  bool IsReusable { get; }
  void ProcessRequest(HttpContext context);
}

There is one property and one method to implement. The property, IsReusable, specifies whether ASP.NET should reuse the same instance of the HTTP handler for multiple requests. My thinking is that, unless there is a specific reason not to, you would always want to reuse the HTTP handler. Unfortunately, I haven't found any guidance suggesting one way or another - at least, not with any real reasoning behind it. The only thing I found was something to the effect of, unless your handler has an expensive instantiation, set IsReusable to false.

The ProcessRequest() method is where you will actually perform the logic to handle the request. Since we're simply reading from the app settings and rewriting the URL, we can handle this in a matter of lines.

HttpHandler.ProcessRequest() Method

public void ProcessRequest(HttpContext context)
{
  // declare vars
  string requestedUrl;
  string targetUrl;
  int urlLength;

  // save requested, target url
  requestedUrl = context.Request.RawUrl;
  if ( requestedUrl.IndexOf("?") >= 0 )
    targetUrl = ConfigurationSettings.AppSettings[requestedUrl.Substring(0, requestedUrl.IndexOf("?"))];
  else
    targetUrl = ConfigurationSettings.AppSettings[requestedUrl];
  if ( targetUrl == null || targetUrl.Length == 0 )
    targetUrl = requestedUrl;

  // save target url length
  urlLength = targetUrl.IndexOf("?");
  if ( urlLength == -1 )
    urlLength = targetUrl.Length;

  // rewrite path
  context.RewritePath(targetUrl);
  IHttpHandler handler = PageParser.GetCompiledPageInstance(
  targetUrl.Substring(0, urlLength), null, context );
  handler.ProcessRequest(context);
}

Now, all we need to do is add the HTTP handler reference in the Web.config file. A lot of people have been falling victim to the following Server.Transfer() error because of incorrect handler configurations, so pay attention to this part.

Error executing child request for [physical page specified in appSettings value].aspx

I'll discuss the reasoning behind the following configuration, but for now, simply replace "*/Pages/*.aspx" with an appropriate path that represents all of the physicalpages (this is veryimportant), MyApp.HttpHandler with the fully-qualified class path of the HTTP handler, and MyApp with the name of the assembly, minus the .dll extension. Also note that the handler for the physical pages must come first. These handlers are checked in order, so if you put it second, then the first path that the request matches will be used, which will probably be your custom handler.

Web.config system.web/httpHandlers Configuration

<system.web>
  <httpHandlers>
    <add
     verb="*"
     path="*/Pages/*.aspx"
     type="System.Web.UI.PageHandlerFactory" />
    <add
     verb="*"
     path="*.aspx"
     type="MyApp.HttpHandler,MyApp" />
    </httpHandlers>
</system.web>

You might also like...

Comments

About the author

Michael Flanakin United States

Michael Flanakin is a principal .NET software architect for the US Air Force and is the president of the Air Force .NET User Group, which serves .NET developers world-wide. Michael provides guid...

Interested in writing for us? Find out more.

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“XML is like violence - if it's not working for you, you're not using enough of it.”