Dr. Dobb's is part of the Informa Tech Division of Informa PLC

This site is operated by a business or businesses owned by Informa PLC and all copyright resides with them. Informa PLC's registered office is 5 Howick Place, London SW1P 1WG. Registered in England and Wales. Number 8860726.


Channels ▼
RSS

Extend ASP.NET Apps Using HttpHandlers


Extend ASP.NET Apps Using HttpHandlers

Download the code for this issue

The majority of developers using ASP.NET think of the web development framework as a page-oriented model, where an application consists of a series of pages that users interact with to store and retrieve information and to initiate processes on the server. Many developers build these applications with little regard for the inner workings of the ASP.NET processing model and the supporting infrastructure. In fact, there is extensive support in ASP.NET's HTTP pipeline based on a set of classes and interfaces that abstract the HTTP protocol, enabling developers to use the ASP.NET infrastructure to harness the power of the underlying protocols to build powerful web-based applications. This same infrastructure provides the functionality that is used for ASP.NET development and web services in the .NET Framework. HttpHandlers are used by ASP.NET to dispatch HTTP requests to user-defined code, and can be used to develop web applications without the use of .aspx files and the typical code-behind model for ASP.NET development, enabling developers to build and extend web-based applications outside of the page-oriented development model. This article will look at the HTTP pipeline and explore the functionality of HttpHandlers.

The HTTP pipeline support in ASP.NET includes important classes and interfaces such as HttpContext, classes that implement IHttpModule, and classes that implement IHttpHandler. The HttpContext class represents the current HTTP request. The classes implementing IHttpModule support the pre- and post-processing of HTTP requests, supporting the filtering and modifications of request and response messages in the HTTP pipeline. The IHttpHandler interface supports the implementation of classes that can service HTTP requests and provide response output. An example of a class derived from IHttpHandler is the Page class of the System.Web.UI namespace. For a typical ASP.NET application developed using Visual Studio .NET, the classes created in the code-behind model for your ASP.NET application are derived from the Page class itself, as shown in the following lines of code:

public class WebForm1 : System.Web.UI.Page
{
    private void Page_Load(object sender,        System.EventArgs e)
    {
// Put user code to initialize the page here
    }
    ...
}

This code is automatically generated by Visual Studio .NET for any new ASP.NET project. As you can see, the WebForm1 class is derived from the Page class. WebForm1 is the code-behind class for the WebForm1.aspx file generated by the IDE. The WebForm1.aspx file includes the following Page directive:

<%@ Page language="c#" Codebehind="WebForm1.aspx.cs" AutoEventWireup="false" Inherits="WebApplication1.WebForm1" %>

This Page directive defines a number of attributes for the ASP.NET web form application, including the code-behind class that the application is to inherit from. In this example, the Inherits attribute of the directive refers to the WebForm1 class, which is derived from the Page class. When developing a web forms application using ASP .NET, the "under the hood" processing ensures that the forms application corresponds to a class library that is derived from the Page class, which implements IHttpHandler. In essence, all ASP.NET applications are driven by an underlying implementation of an HttpHandler.

The default configuration for an ASP.NET application is defined by the machine.config file for the .NET Framework installation, and includes the mappings of some standard handlers to certain file extensions. The following entry can be found under the <httpHandlers> element of the machine.config file:

<add verb="*"path="*.aspx" type="System.Web.UI.PageHandlerFactory" />

The file entry in machine.config maps the .aspx file extension to the PageHandlerFactory class, which is an HTTP handler factory class that can compile the source code referenced in an .aspx file into a class derived from the Page class in System.Web.UI. Since the Page class implements the IHttpHandler interface, the instance of the object that is instantiated by the page request can be processed in ASP.NET's HTTP pipeline. This configuration is also used to support the mapping of other extensions, such as the .asmx extension used for ASP.NET web services. Many other file extensions are mapped to various handlers by default at the machine configuration level.

HttpHandler Architecture

Along with the support for standard handlers in ASP.NET, there is functionality to build and extend applications using custom handlers. To build a custom handler class it must be derived from the IHttpHandler interface, as shown by the following lines of code:

    interface IHttpHandler
    {
          void ProcessRequest(HttpContext ctx);
          bool IsReuseable { get; }
    }

The ProcessRequest method is called to process the current request and provide a response. The IsReuseable property defines whether or not the handler can be used by more than one request simultaneously. The following code demonstrates the implementation of a sample of a handler class:

    public class MyHandler : IHttpHandler
    {
        public void ProcessRequest(HttpContext ctx)
        {
            ctx.Response.Write("Response from MyHandler.");
        }
        public bool IsReusable 
        { 
            get { return true; } 
        }
    }

To map an incoming request to a handler, the Web.config file for the application's virtual directory can be used to configure handlers for that application. For the sampler handler class just mentioned, the Web.config file would be modified to include the following section:

    <configuration>
         <system.web>
          <httpHandlers>
           <add verb="GET" path="*.handler"
         type="Handlers.MyHandler, Handlers"/>
          </httpHandlers>
         </system.web>
    </configuration>

This configuration file addition adds the support for an HttpHandler supporting a GET request with the extension .handler. This request will then be processed by Handlers.MyHandler, located in the Handlers assembly. For this custom handler to work, there are a couple of deployment and configuration details to cover. First, the assembly containing the handler class must be located in the bin subdirectory of the virtual directory for the web application, or it must be installed in the GAC for the ASP.NET worker process to be able to locate it. Second, for a handler to be invoked for a particular file extension, that extension must also be mapped to the aspnet_isapi.dll in the IIS application mappings settings, as shown in Figure 1. The extension must be mapped to an executable or library file as shown in Figure 2. The application mapping can also be set up to validate the existence of the file requested for the extension. For the sample URL http://localhost/VDIR/ my.handler, the request would be handled by the MyHandler class. The path attribute of the add element under <httpHandlers> can have a value assigned at the extension level, as shown by the path="*.handler" value, or it can be mapped a specific path such as path="my.handler".

Building Custom HttpHandlers

For many web applications, such as those focused on dynamic delivery of content from disparate data sources like relational databases, XML files, and serialized object stores, the development and maintenance of a traditional page-oriented web site can be tedious and time consuming. With the HttpHandler architecture and HTTP pipeline support of ASP.NET, you can build a flexible application while avoiding traditional page-oriented web development.

To demonstrate the power of the HttpHandler architecture, I'll walk through the development of a simple handler application that processes requests that are mapped to specific files and are processed by custom handler classes. The first handler will be mapped to the .xml file extension, and will process the file name of the requested URL, load the corresponding XML document into a DataGrid, and render the HTML output of the DataGrid to the Response instance. The code for the .xml extension handler is shown in Listing 1. When this handler is executed, the RenderControl method of the DataGrid populates the HtmlTextWriter object with its HTML output. The StringWriter instance passed to the HtmlTextWriter's constructor is then passed to the Response's Write method, writing the output back to the browser. To test the handler, deploy it following the process previously outlined and use the following Vendors.xml file to test the process:

<Vendors>
    <Vendor>
          <VendorID>1</VendorID> 
          <Name>Acme Corporation</Name> 
          <Telephone>314-555-1212</Telephone> 
      </Vendor>
    <Vendor>
          <VendorID>2</VendorID> 
          <Name>Smith and Company</Name> 
          <Telephone>212-555-1212</Telephone> 
      </Vendor>
</Vendors>

Using the XmlHandler class from Listing 1, you can write this output in the form of an HTML table back to the browser, displaying the contents of the XML file in a grid form.

With HttpHandlers, there are some other interesting ways that you can handle the request and process the output to a requesting client. By modifying the XmlHandler class previously demonstrated, you can process requests for the XML files on your server and provide the output as Excel. In this example, the .xls extension used for Excel files will be mapped to the new handler class. You can then use a URL to request a file that's in XML form on the web server, such as http://localhost/ContentHandler/ Vendor.xml, but replace the .xml file extension with .xls. This type of request filtering and processing is ideally performed using an HttpModule, but to simplify this example, the logic to modify the file extension will be included in our HttpHandler. The following lines of code show the modified ProcessRequest function from the class:

public void ProcessRequest(HttpContext ctx)

{
        ...

      //Manipulate Request.PhysicalPath       //string value to replace file extension
        fileRequested = fileRequested.Replace 		    (".xls", ".xml");

         ...

        //Set ContentType for HttpResponse
        ctx.Response.ContentType =            "application/vnd.ms-excel";

        //Provide response output using the 
        //StringWriter instance utilized by 
        //the HtmlTextWriter constructor
        ctx.Response.Write(sw.ToString());

        ctx.Response.End();
}

The modified function replaces the file extension in the string that maps the path of the requested file on the server, and then the XML file is processed just as it was in the previous XmlHandler example. The file is processed by the handler, loaded into a DataGrid, and rendered to HTML. But prior to writing the response output to the browser, the ContentType property of the Response instance is changed to Excel. This causes the browser to detect the Excel content type and then prompts the user to save the output to an Excel file or to view the output using Excel within the browser.

Rendering Pages Dynamically

While the two handler classes previously demonstrated support of the processing and output of XML data to the browser without the use of .aspx pages, the output has been different than what you would see on most web sites, since the output hasn't been in a traditional web page form. When a request is processed for the Vendors XML file using the XmlHandler from the previous example and the source from your browser is viewed, you see the following output:

<table cellspacing="0" rules="all" border="1" id style="border-
    collapse:collapse;">
    <tr>
        <td>VendorID</td><td>Name</td><td>Telephone</td>
    </tr><tr>
        <td>1</td><td>Acme Corporation</td><td>314-555-1212</td>
    </tr><tr>
        <td>2</td><td>Smith and Company</td><td>212-555-1212</td>
    </tr>
</table>  

Note that the source is raw HTML for the table formatted by the DataGrid and does not conform to HTML standards for a web page. This is not suitable for building a web application that provides the user with a good visual experience or extends functionality beyond the delivery of static content. That's where the Page class from the System.Web.UI namespace becomes very useful. By creating an instance of the Page class, you can build a web page output with controls, page formatting, and other dynamic elements all from within a server-side assembly. This Page object can then be rendered to a client browser using HttpHandlers, eliminating the need for .aspx files. The code in Listing 2 demonstrates the use of the Page class. The SimplePage class overrides the Render method and builds on the previous examples by processing an XML file into a DataGrid. The Page class can be implemented in an HttpHandler as demonstrated by the following code:

public void ProcessRequest(HttpContext ctx)
{
        string fileRequested =            ctx.Request.PhysicalPath.ToString();

       //Manipulate Request.PhysicalPath string        // value to replace file extension
        fileRequested =            fileRequested.Replace(".page",            ".xml");

        //Create instance of the          // HtmlTextWriter to be populated by          // DataGrid's HTML output
        StringWriter sw = new StringWriter();
        HtmlTextWriter htmlTW = new            HtmlTextWriter(sw);

        SimplePage myPage = new            SimplePage(fileRequested);

        //Render HTML for page object
        myPage.RenderControl(htmlTW);

        //Provide response output using the         // StringWriter instance utilized by          // the HtmlTextWriter constructor
        ctx.Response.Write(sw.ToString());
}  

Using the RenderControl method of the custom Page class, the output of the page can be written to the Response instance. By mapping the .page extension to a new handler, you can extend the previous example to process the XML content and provide the output in a web page format, as shown in Figure 3. Although this example is very simple, by deriving from the Page class you can build a Page output that takes advantage of the extensive support for controls, page formatting using CSS classes, and other programmatic HTML rendering features.

By using the extensive customization support in the HTTP pipeline for ASP.NET, including the support for HttpHandlers, developers can build web-based applications without using the traditional page-oriented model for web site development. Developers can leverage System.Web.UI.Page class implementations and the extensive ASP.NET customization features so that feature-rich applications can be designed and deployed without relying on the ASP.NET code-behind model that's supported by Visual Studio .NET. These features allow developers to better build and deploy web-based applications without the fragility of hand coding and tweaking HTML, while also providing a framework to easily manage the processing of dynamic content from a variety of sources in your web applications. w::d


Randy Holloway is the founder of Winformation Systems, a technology consulting and training initiative specializing in the development of enterprise systems in the Windows environment and web services technologies. Contact Randy at articles@ winformationsystems.com.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task. However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.