Implementing HTTP Handlers in ASP.NET

Redirect, Transfer or Rewrite?

Earlier, we implemented a URL rewriting scheme; however, in some circumstances, you may wish to implement a Response.Redirect() or Server.Transfer() instead. One reason to do this is to forward from Default.aspx to another page, like Home.aspx. You may or may not want to do this "behind the scenes," but that is a decision for you to make yourself. As always, each option comes with its own set of pros and cons.

Redirects are essentially two GET (or POST) requests. The first request gets processed and then the Response.Redirect() sends a response back to the client with an HTTP 302 redirect command. This obviously causes a slight delay as the second request gets processed. Also, since the new URL is sent to the client, it won't be hidden. This clearly doesn't support URL beautification, which is the main reason most people implement handlers. Even though redirects won't solve your problems, they still play an important part in the overall solution and should be considered when developing your mapping solution.

Transfers, unlike redirects, keep control within the application; but, they are still treated as two requests. The difference is that instead of the client handling the HTTP 302 redirect command, the web server handles it. This means that any modules, as well as the handler, will be processed twice . There are three key things to remember when using transfers: (1) the Request object, and all of its properties and methods, will reflect the initial request (logical page) and not the physical page; (2) post-back will not work; and, (3) in order to use the transfer you have to have two handlers specified in the Web.config file. There might be a way to get the post-back to work, but I don't know what that would entail. Perhaps I will delve into the ASP.NET request process fully one day. As for the two handler issue, let me explain that in a bit more detail. As you may remember from above, you specified two handlers in the Web.config file. The reason for this is because after the Server.Transfer() is executed, ASP.NET will send the second request back through the handler. I'm not completely sure why this is a problem, but it is. So, to fix it, you need to have some way to identify what requests should be handled by ASP.NET's default handler and which should be handled by yours. I attacked this by putting all of my physical pages in a Pages directory. So, by re-adding the default handler to handle all requests to "*/Pages/*.aspx", we tell ASP.NET how to support each type of request. As I also mentioned before, this will fix the the "Error executing child request" error.

Rewrites provide the best performance because there is no back-tracking to re-handle requests. You simply change the URL and continue on with the request processing. Know that accessing the Request object will now reflect the new (physical) URL and you will not have access to the old (logical) URL. You can get around this by adding custom variables to the HttpContext, but that shouldn't be necessary for most situations.

To add support for redirects and transfers, we can simply change our Web.config file by prepending "redirect.", "transfer.", or "rewrite." to identify how we want the request handled. Then, update the IHttpHandler.ProcessRequest() method to treat them accordingly.

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;

  // handle type 
  if ( targetUrl.StartsWith("redirect.") )
  {
    context.Response.Redirect(targetUrl.Substring(9));
  }
  else if ( targetUrl.StartsWith("transfer.") )
  {
    context.Server.Transfer(targetUrl.Substring(9));
  }
  else
  {
    // if type is specified, remove it
    if ( targetUrl.StartsWith("rewrite.") )
      targetUrl = targetUrl.Substring(8);

    // 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);
  }
}

Conclusion

Well, congratulations on your first HTTP handler implementation. There is plenty of room for improvement, so try to think of how you can manage the mappings to add more than just a simple URL rewriting scheme. One thing that you might want to consider is a post-back processing component. Yes, post-back is handled by ASP.NET, but performance can be increased by removing that overhead. Anyway, my point is that there are a lot of things you can do to improve this simple implementation. I encourage you to add to this and let me know how well it works out for you. I'd be interested to hear some of the things people are doing with handlers. Good luck!

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.

“Nine people can't make a baby in a month.” - Fred Brooks