Logo_HttpModuleHandlerIntro_16x.gif Implementing HTTP Handlers in ASP.NET

As I mentioned before, it was hard to find information on how to setup an ASP.NET HTTP handler when I first started out. Of course, after time, I've found a wealth of articles, posts, and comments from others on these and other related topics. As my second post in the HTTP module/handler saga, I hope to give you an in-depth discussion on the topic of handlers to include pros, cons, and a sample implementation that you can extend.

Introduction

ASP.NET uses HTTP handlers to process all requests. For general information on HTTP handlers (and modules), see my previous article, Introduction to ASP.NET HTTP Modules and Handlers. In the following sections, I will discuss a few pros and cons of HTTP handlers and provide a step-by-step guide to implementing your own handler.

Most notably, HTTP handlers are beneficial because they provide a way to interact with HTTP requests before they get to a web page. This can be very nice, depending on what you need to do with the request. For instance, perhaps there is a need for logging actions taken by users or controlling access to individual files (i.e. images, executables). For the purposes of this article, I will discuss using handlers for URL rewriting.

This is intended to be a work in progress, so let me know if there is something extra that you'd like to see in it, or, if there are any mistakes/inconsistencies. Any other feedback is welcome, as well.

Setup Configuration Settings

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:

<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 key must 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.

Create 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.

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.

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 physical pages (this is very important),  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.

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

Handling Post-Back

Now that we have our URL rewriting in place, it's time to do some real work. Based on this section's title, you've probably figured out that you're going to have some post-back issues (if you haven't already tested that out). The problem with post-back is that, when rendered, the HtmlForm object sets the action to the physical page name. Of course, this means that when you submit the form, your true page is displayed. This is obviously less than ideal for URL beautification. Not to mention it would most likely confuse your users. Well, there are two solutions to consider.

First, you can add a simple script block to fix the problem. This is the easiest solution, but there's one problem: if a user has scripting turned off (as if that is ever the case, anyway), the fix will be nullified. But, in case you still like this solution (I do), add this code to your Page class. If you don't already, I'd suggest creating a base Page object for all of your pages to implement. Then, add this code to the base Page class. This allows you a good deal of extensibility as far as adding common features easily.

RegisterStartupScript( "PostBackFix", 
  "" );

Your second option is to extend the HtmlForm class. This is pretty simple, as you will see below, but it comes with its own issues. The main problem that I have with this solution is that you have to explicitly add the extended HtmlForm object to replace the default HTML form tag. Not that it is hard to do, but it can get tedious if you're creating (or converting) a lot of pages.

public class ActionlessForm : HtmlForm
{
protected override void RenderAttributes(HtmlTextWriter writer)
{
Attributes.Add("enctype", Enctype);
Attributes.Add("id", ClientID);
Attributes.Add("method", Method);
Attributes.Add("name", Name);
Attributes.Add("target", Target);
Attributes.Render(writer);
}
}

Each method has it's own pros and cons. They're pretty simple to understand, so the decision shouldn't be too hard. Honestly, you can implement the second option through a base Page class, but that adds a lot more complexity to your system then you're probably looking for. Explore your options and be innovative.

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.

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!

How would you rate this item?
  1 2 3 4 5 6 7 8 9 10  
Poor   Excellent
Comments, recommendations or suggestions.(optional)
This item has been rated by 89252 people
Average rating:
3 out of 10
1 2 3 4 5 6 7 8 9 10  
Rating Summary
8/11/2006 10:58:52 AM by Anonymous
8/27/2006 10:46:20 PM by Anonymous
This article was very useful as this was my first http handler and I was literally struggling to create my first handler in .net 2.0. Thanks a lot for the code. Sure I would also try to dig deep into http handlers.
8/29/2006 1:53:07 AM by Anonymous
Nice article! I'm probably too tired to think, but I'm not sure I understand what the Context.RewritePath(targetUrl) does given your code. If I remove the line, what changes?
12/19/2006 2:56:09 PM by Anonymous
12/25/2006 3:08:48 AM by Michael Flanakin
Context.RewritePath(string) changes the URL from the fake page to the real page. Without it, you'd have to use real paths.
1/16/2007 4:56:10 AM by Anonymous
its great learning tutorial with a single page. Thanks.
3/11/2007 10:06:59 PM by Anonymous
4/18/2007 8:08:45 AM by Anonymous
5/29/2007 12:29:08 AM by Anonymous
Thanks - I'll ned to read this a few more times to get the full meaning but I think that its very valuable to have this information available - in a readable and understndable form. Thankyou for your time and effort - its appreciated
9/21/2007 12:26:19 AM by Anonymous
4/19/2008 2:39:31 PM by Anonymous
Your site is great! Very useful. Good resources here. Thanks much!
4/19/2008 2:39:40 PM by Anonymous
Your site is great! Very useful. Good resources here. Thanks much!
12/10/2009 3:17:37 PM by Anonymous
1/3/2010 7:07:19 AM by Anonymous
Very nice site!
2/8/2010 4:56:11 AM by Anonymous
6/30/2010 9:53:44 PM by Anonymous
9/20/2010 5:15:37 AM by Anonymous
9/21/2010 1:28:47 AM by Anonymous
4/2/2012 8:17:27 PM by Anonymous
5/25/2012 8:00:13 AM by Anonymous
7/16/2012 12:38:20 AM by Anonymous
It's good to get a fresh way of loiokng at it.
12/30/2013 12:31:55 PM by Anonymous
Aggregate kamp pounds ca £ 5 . De er peis avvisende , vannforsyning - rebarbative , råtebestandig , mugg - avstøtende , i tillegg til ikke - allergifremkallende . Slike Plass til kan kjøpes med noen unike Gillie patternswoodland , mosegrodd , grønne green , eller ørken ) . Man kan velge mellom VI unike BDU camopatterns ( skog lager , digital skog , halvt dusin colouration ørken , digital ødemark , mange land digitalkamera , andmulticam ) . Størrelsene varierer fra mindre stødig å hjelpe 3extra sinnet extended.When forhold til troen i tett rett ned crownwork i det siste , klapp ( p ) utendørs jakker tendens til å være dilutant , pram , tørrere pluss mer utmerket i denne vintervær . Praktisk talt de har flere farger , men i tillegg vil være mer elegant gjennom modeller. I tillegg til disse menneskene har en bedre kalori effekt ved å ha gamey - tekniske tekstiler i tillegg til teknologisk know -how i dem . I løpet av det siste, lavere crownwork godkjent sunn i tillegg til micromillimeter metoder , gjør nesten alt modellen et forsøk på den vakreste å gjøre hele kroppen undertrykke helt gjennom fløyel - teksturert sammen med høyere teknologisk arktisk ordning . I tillegg , netto salg gjort med Parajumpers problem forbundet med Parajumpers jakke krone er vesentlig rimeligere . Snowboardere kan nyte spillet ville ikke kunnskap noen risiko , de fleste mennesker kommer til å være takknemlig for at en flott jakke krone . parajumpers usaf http://www.ilpozzodigiacobbe.it/it/parajumpers.asp
12/30/2013 12:31:59 PM by Anonymous
Men hvordan skille mellom sant og usant Of Parajumpers jakker Logo klær produkter må oppfylle standardene , bør det være følgende : produsentens navn og adresse , produktnavn , varemerke , modellspesifikasjoner , bruk av råvarer , komponenter og innhold , vaskemetoder , implementering av standarden produktnummer , produktkvalitetsnivåerog produktkvalitet inspeksjon sertifikat . Rett ned klær er bekymret, bør det merkes ned filler mengde kasjmir , ned typer og grå og hvit farge , lade ned mengden , osv. Hvis en dunjakke logo er ikke komplett , mangler elementer mer , bør tiltrekke seg oppmerksomhet . PJS online salg jakker http://www.weecamp.no/articles/parajumpers.asp
12/30/2013 12:32:54 PM by Anonymous
Michael Flanakin :: The Articles :: Implementing HTTP Handlers <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">uggs outlet</a></b> olmffqmiyj <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">uggs outlet online</a></b> qajrxtkz <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">uggs online</a></b> mgutza <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">uggs kopen</a></b> pbqhnxingzo <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">ugg sale</a></b> apjprdoe <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">ugg sale online</a></b> xcpemsvvvpl <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">ugg outlet</a></b> fgwsynw <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">ugg outlet online</a></b> fgxyrqtbssn <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">ugg online</a></b> ylquac <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">sale uggs</a></b> Ugg Classic Tall http://embhonpe.org/En/CyberMondayUgg/CyberMondayDealsUggs/ugg-australia-classic-tall.html
12/30/2013 12:35:19 PM by Anonymous
Michael Flanakin :: The Articles :: Implementing HTTP Handlers <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">kinder uggs</a></b> gpkwevt <b><a href="alt85.com/NL/UGG/KinderUggsKopen/#">kinder uggs sale</a></b> nxpzhjmg <b><a href="alt85.com/FR/UGG/UggsFrance/#">Uggs soldes</a></b> webfkzkwck <b><a href="alt85.com/FR/UGG/UggsFrance/#">Uggs pas cher</a></b> djynoc <b><a href="alt85.com/FR/UGG/UggsFrance/#">Uggs france</a></b> zgfgdz <b><a href="alt85.com/FR/UGG/UggsFrance/#">Uggs bottes</a></b> houvun <b><a href="alt85.com/FR/UGG/UggsFrance/#">Uggs australia pas cher</a></b> pbfovmo <b><a href="alt85.com/FR/UGG/UggsFrance/#">ugg bottes</a></b> idjctmqhkha <b><a href="alt85.com/FR/UGG/UggsFrance/#">ugg australia soldes</a></b> jqogfccost <b><a href="alt85.com/FR/UGG/UggsFrance/#">soldes Uggs</a></b> christian louboutin shoes on sale http://www.homebasednetworks.com/
Page 1 of 3571First   Previous   [1]  2  3  4  5  6  7  8  9  10  Next   Last   


Page Options Page Options
Average rating:
3 out of 10
Rate this module
Email this page

History History

1.0Genesis
1.0.1Set target URL to the requested URL when target URL not found
1.0.2Updated app setting key-value pairs to correct required paths
1.1 Added Web.config code to specify HTTP handler
Updated ActionlessForm.RenderAttributes() method
Updated both HttpHandler.ProcessRequest() methods
1.1.1Added second handler to Web.config to handle physical pages and described the reasoning for this in more detail