Implementing HTTP handlers in ASP. NET

Source: Internet
Author: User
Tags configuration settings vars

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 shocould be coded, you shocould 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 cocould technically put them in a database; which wocould 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 wocould assume that the latter wocould be true in most situations. you shoshould 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>

TheKeyIs intended to be the requested page andValueIs the physical page that will be displayed. Pretty simple. Two important things to note are that, using this simplified scenario,Key MustBe andValueShocould be root-relative paths .. For instance, the above specifies thatHttp: // localhost/MyApp/logicalpage1.aspxWill actually mapHttp: // 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 wocould 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,LogactionClass for logging orRewriteurlClass 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 shoshould take a look atIhttphandlerInterface, 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 shocould reuse the same instance of the HTTP handler for multiple requests. my thinking is that, unless there is a specific reason not to, You wowould 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, SetIsreusableTo false.

TheProcessrequest ()Method is where you will actually perform the logic to handle the request. Since we're 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) 
{< br> // declare vars string requestedurl;
string TargetUrl;
int urllength;
// save requested, target URL requestedurl = context. request. rawurl; If (requestedurl. indexof ("? ")> = 0)
TargetUrl = configurationsettings. receivettings [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 followingServer. Transfer ()Error because of incorrect handler submissions, 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. 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,HtmlformObject 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 wocould 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 yourPageClass. If you don't already, I 'd suggest creating a basePageObject for all of your pages to implement. Then, add this code to the basePageClass. This allows you a good deal of extensibility as far as adding common features easily.

 
Registerstartupscript ("postbackfix ",
"");

Your second option is to extendHtmlformClass. 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 extendedHtmlformObject 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 demo-shouldn't be too hard. Honestly, you can implement the second option through a basePageClass, but that adds a lot more complexity to your system then you're probably looking for. Need e your options and be innovative.

Redirect, transfer, or rewrite

Earlier, we implemented a URL rewriting scheme; however, in some circumstances, you may wish to implementResponse. Redirect ()OrServer. 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 demo-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 thenResponse. Redirect ()Sends a response back to the client with an HTTP 302 redirect command. this obviusly causes a slight delay as the second request gets processed. also, since the new URL is sent to the client, it won't be den. 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 shocould 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 beProcessed twice . There are three key things to remember when using transfers :( 1 ) 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 wowould 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 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 shoshould be handled by ASP. net's default handler and which shoshould 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 "*/Pages/*. aspx" , We tell ASP. NET how to support each type of request. As I also mentioned before, this will fix 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 accessingRequestObject 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 toHttpcontext, 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, updateIhttphandler. 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. receivettings [requestedurl. substring (0, requestedurl. indexof ("? ")];
Else TargetUrl = configurationsettings. receivettings [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 poi NT 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!

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.