Wednesday, January 7, 2009

Understanding the ASP.NET Architecture - Part 2

HTTP Handlers


HTTP handlers implement a common interface called IHttpHandler (a member of the System.Web namespace).

There are two kinds of handler classes:
  • Handler processing classes: These are classes that implement the interface (IHttpHandler) that allows them to process HTTP requests. For example, the Page class is a handler that represents an *.aspx Web page.
  • Handler factory classes: These are classes that dynamically manufacture new handler classes. These classes implement the IHttpHandlerFactory interface. For example, the PageHandlerFactory class generates a Page handler class for every HTTP request that calls an *.aspx Web page.
The IHttpHandler interface defines a method called ProcessRequest(), which accepts an HttpContext instance as its argument. The interface also provides a Boolean property called IsReusable(), which dictates whether an HTTP handler can pass a request to another handler:


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

The HttpContext object encapsulates all of the HTTP-specific details for an individual HTTP request. Clearly, the ProcessRequest() method provides a clean way of passing a client's request details between different objects on the Web server.

HTTP handlers must be registered in the ASP.NET configuration files (either Machine.config or Web.config). This is an excerpt from the Machine.config file:

<httphandlers>
  <add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler">
  <add verb="*" path="*.aspx" type="System.Web.Handlers.TraceHandler">
</httphandlers>

The <httphandlers></httphandlers> section registers HTTP handlers on the server using child elements. A handler class can process an individual Uniform Resource Identifier (URI) or a group of URIs that share a common file extension. The Machine.config file excerpt shown previously illustrates both options. The attributes for the element are as follows:
  • Verb: This is a comma-separated list of HTTP verbs, including GET, POST, PUT, or the wildcard asterisk (*).
  • Path: This is a single URI path or a wildcard path, for example, *.aspx.
  • Type: This is a class/assembly combination that contains the handler class. The excerpt only shows the class name, but you can append the assembly name as well, separated by a comma.
By now you can begin to appreciate how extensible the ASP.NET runtime engine is. You can route HTTP requests to any handler you set up. Few readers will ever need to create a custom handler because you can almost always tackle a specific HTTP request through the Page object (discussed next). But in other cases, HTTP handlers are the most efficient way to handle an HTTP request because they can service a request without going to the expense of loading up a Page object.

Let's look at an interesting example. Consider a Web application that logs the Internet Protocol (IP) address and a timestamp of all clients when they first access the application. Let's say that the application provides an entry page, called gateway.aspx, that provides no user interface but that records the client's IP address with a timestamp and then redirects the client on to a formal login page.

You could create a standard *.aspx Web page that performs this function, but this approach unnecessarily creates an instance of the Page object (assuming you have not altered the standard HTTP handler for .aspx pages). A better approach would be to create a custom HTTP handler that processes the gateway.aspx page directly. If you want even more distinction, you could create a custom file extension, such as .xyz, for the gateway page.

Listing 1: A Custom HTTP Handler:

public class oHandler : IHttpHandler
{
    public void ProcessRequest(HttpContext context)
    {
        // Instance an EventLog object
        EventLog objEvt = new EventLog();
        try
        {
            //Write the client's IP address to the event log,
            //with a timestamp
            string strClientIP = context.Request.UserHostAddress;
            objEvt.Source = "ASP.NET";

            // Event source is ASP.NET
            objEvt.WriteEntry("Client IP: " + strClientIP + " logged at: " + Now);

            //Redirect the client to the login page
            context.Response.Redirect("login.aspx");
        }
        catch (Exception err)
        {
            //No action taken. Prevents unhandled errors if
            //.WriteEntry fails.
        }
    }

    public bool IsReusable
    {
        get
        {
            return (true);
        }
    }
}

Listing 1 is very simple. The client's IP address is extracted using the Request object's UserHostAddress property. Next, the handler writes a record into the system event log. Finally, the handler redirects the user to the formal login page, called login.aspx. Clearly, the HttpContext object is critical for an HTTP handler class to work!

Notice that Listing 1 includes exception handling around the WriteEntry() method in case this method fails. The exception handler does not do anything in that it does not take a specific action when an exception occurs. However, it does prevent unhandled exceptions, which could potentially bring down the Web site.

This may happen if the site administrator fails to reconfigure the event logs from the default overwrite after 7 days to overwrite as needed. If the event logs fill up completely, then a write failure will cause an exception in the code, with potentially adverse effects. This is of particular concern in high-volume sites that write a lot of information to the event logs.

Next, you must register the HTTP handler class in the Web.config (or Machine.config) file:

<httphandlers>
  <add verb="*" path="gateway.aspx" type="oHandler">
</httphandlers>

The syntax for the class name and the assembly is important to get right; otherwise the Web application will generate runtime errors when you attempt to load it in the browser.


Finally, you can test the HTTP handler by opening a new browser and typing the path to the gateway page—in this case, http://localhost/WebSite1/gateway.aspx. The page will load, then after a couple of seconds you should be redirected to the login.aspx page. Open the application log in the Event Viewer, and you will see a recent information record with something like the following contents:
Client IP: 127.0.0.1 signed in at: 1/1/2009 7:19:45 PM.


The most interesting aspect of this example is that the page gateway.aspx does not exist. We never added one to the ASP.NET project, nor do we need to add one. The HTTP handler recognizes the path name and takes that as an indication to start working. The actual page does not need to exist, and this is what makes HTTP handler classes so efficient under certain circumstances.

Quoted.

No comments:

Post a Comment