CoreCoder
LANGUAGES: C#
ASP.NET VERSIONS: 1.0 | 1.1
Custom HTTP Handlers
Harness the Power for Yourself
By Dino Esposito
As a brilliant ASP.NET developer, and a loyal reader of asp.netPRO magazine, you know that any page request (or Web service request) is picked up and serviced by a specialized HTTP handler component. Whenever a request comes in, IIS looks up its metabase to find a match between the verb and file extension of the requested URL and any components registered to handle incoming requests.
For example, when an .aspx request comes in, IIS knows from the metabase that the request should be routed to a component named aspnet_isapi.dll. The aspnet_isapi.dll module is an old-style, Win32 DLL that provides the common skeleton of ISAPI modules. (ISAPI modules are Win32 DLLs that add custom functionality to IIS.)
All
requests routed to the ASP.NET ISAPI module undergo a second filter: ASP.NET
HTTP handlers. The ASP.NET runtime checks the verb and path of the URL, and
dispatches the request to a selected HTTP handler. For each application, the
list of supported HTTP handlers is hard-coded in the configuration files. The
list of standard HTTP handlers can be found in the
Overall, an HTTP handler is the managed component that takes care of serving ASP.NET requests that match a given range of criteria, such as URL formatting, extension, path, and query string. In this article, I'll briefly discuss the basics of HTTP handlers and illustrate a sample handler that I find extremely useful to develop and test Web applications.
Learn to Use Handlers
Speaking of HTTP handlers, one common question goes like this: Will handlers be helpful to implement feature X? Where "feature X" is, for whatever reasons, difficult for developers to implement quickly and efficiently. Golden rules for understanding the real goal and scope of HTTP handlers can be summarized as follows:
- HTTP handlers are managed classes used to service certain Web requests.
- HTTP handlers represent an optionally virtual URL.
- The base ASP.NET Page class is an HTTP handler.
- The HTTP handler merely and uniquely processes an incoming request.
- HTTP handlers have neither access nor influence on application-level events, such as BeginRequest, AcquireSessionState, or PreSendRequestContent.
If your application is only concerned with ASPX pages, you don't need to care much about HTTP handlers. Handlers are quite helpful to reference any server resources through a URL. For example, you can use an HTTP handler to generate dynamic images and bind them to the tag. The tag is the only HTML element that can display images on the Web, and it only accepts images via a URL. By using a custom HTTP handler, you can return an image created programmatically using GDI+, retrieved from a database using ADO.NET, or simply retrieved from the ASP.NET cache.
Should you use a handler or just an ad hoc page for this and similar purposes? Note that the ASP.NET Page class is a handler itself, albeit a pretty rich and sophisticated one. As far as image generation is concerned, you don't need the viewstate or postback management that are hard-coded in the Page class. For this reason, using a custom HTTP handler is a preferable approach from a performance standpoint.
Just as HTTP handlers are employed to service a given request, they can be used to deny any undesired request. For example, type the URL to your application's web.config file in the browser's address bar. You get an error page. To block user access to a given set of resources, you can tweak your application's web.config file, as follows:
type="HttpForbiddenHandler"/> The path
attribute indicates which resources will be blocked; more in general, it
indicates which resources, and for which verbs, will be subject to the
specified HTTP handler component. The HttpForbiddenHandler class is a
built-in handler defined in the System.Web namespace. The preceding
configuration script is extracted from machine.config, but can be adapted and
employed in any application. Another
interesting example of system HTTP handlers is trace.axd. Trace.axd is the name
of an ASP.NET utility used to track the trace logs of a given page or
application (see Figure 1). You call trace.axd by appending the name of the
handler to the application's name. Although trace.axd is used as the name of a
server-side resource, it's a purely virtual resource; no file or folder exists
on the Web server with that name. So
what's up? IIS picks up the request for trace.axd and forwards it to ASP.NET,
without verifying that a resource with that name really exists. ASP.NET, in
turn, routes the request to the HTTP handler of choice. In the end, the handler
gets to work on a request that takes the form of a URL resource. Most HTTP
handlers, including the handler for ASPX pages, assume that a file lives behind
that URL, map the path to the physical file, and do something with it. However,
as long as the URL carries enough information for the selected HTTP handler,
there's no need to resort to a server-side file. In other words, the URL can be
seen as the command that triggers the handler. In this perspective, the command
can be anything that the handler can understand - including, but not limited
to, a server file name. Sample Handlers The list
of useful things you can do with HTTP handlers is only limited by your
imagination. Through a well written handler, you can have your users invoke any
sort of functionality via the Web. An inspiring MSDN article that shows
creative uses of HTTP handlers is http://msdn.microsoft.com/library/en-us/dnaspp/html/httphandl.asp.
In particular, I'd like to mention the following features that can be
effectively coded through handlers: By the
way, you'll find all these features implemented at various levels and in
various ways in ASP.NET 2.0. To demonstrate the power of handlers, I'll show
you a couple of HTTP handlers that I use often to inspect code and form a quick
idea of how pages that I've never seen before are laid out and function (when I
don't have the Visual Studio.NET project available). Outline the Handler As
mentioned, an HTTP handler is a managed class that implements the IHttpHandler
interface. The sample handler is named SourceHandler and is outlined as
follows: public class
SourceHandler : IHttpHandler { public SourceHandler() { ... } // Override the ProcessRequest method public void ProcessRequest(HttpContext
context) { ... } // Override the IsReusable property public bool IsReusable { get { return true; } } } The IHttpHandler
interface is made of two members: ProcessRequest and IsReusable.
The former is a method and takes the HTTP context of the ongoing request,
processes the HTTP payload, and generates the response for the caller
(typically, but not necessarily, the browser). The response is generated by
writing to the output stream using the familiar Response object either
directly or through the rendering engine of Web server controls. The IsReusable
member is a read-only property indicating whether another request in the same
application can use the current instance of the handler. The ProcessRequest
method represents the core of each handler. For
ASP.NET handlers to work, two preliminary requirements should be met. First and
foremost, you must ensure that IIS routes to ASP.NET all requests you want the
handler to process. Second, you must configure ASP.NET to forward those
requests to your handler. You decide which extensions the handler will manage.
Next, you edit the IIS metabase so that IIS forwards those requests to
aspnet_isapi.dll - the ASP.NET entry point in the IIS space (see Figure 2).
After you've done this, all requests for, say, a .mydata resource that
originate in an application will be routed to the ASP.NET AppDomain of the
specified application. To redirect these requests to the handler, edit the
web.config as follows: type="AspNetPro.YourHandler,
YourHandler" /> The
.mydata resource doesn't have to be a server file, but its invocation triggers
the handler and generates a response. Likewise, you can register the handler with
a particular URL. For example, the trace viewer is activated if the target URL
of the call is trace.axd. The
sample handler, named SourceHandler, is triggered by a call to
source.axd and returns the source code of the specified ASPX file. This handler
is useful during the development cycle to view the source of a page without
accessing the Visual Studio.NET project. The typical call to the handler is
shown here: http://localhost/.../source.axd/page.aspx For
obvious reasons, the handler works on local requests only. The handler finds
the name of the page to process in the query string, or appended to the URL.
Figure 3 shows the full source. The ProcessRequest method gets the URL
of the request, and ensures the host is local. Then it cuts the source.axd
string from the URL and maps the resulting string to a server path. Finally, it
opens the .aspx source file using a stream reader, and copies the contents into
a read-only text area (see Figure 4). public class
SourceHandler : IHttpHandler { public SourceHandler() { } // Override the ProcessRequest method. public void ProcessRequest(HttpContext
context) { // Check if local if (!context.Request.Url.IsLoopback) { context.Response.Write( "Unable to process the
request."); return; } // By design, the URL has the form:
xxx.axd/page. string url =
context.Request.Url.ToString(); // Check the working mode
(source/compiled). string output = ""; if (url.IndexOf("/source.axd/")
>0) output = RetrieveAspxSource(context.Request.Url); else { context.Response.Write( "Unable to process the
request."); return; } // Output. context.Response.Write(" context.Response.Write("
Figure 1: Trace Viewer lists the requests to
a given application with trace log details.
Figure 2: Edit the IIS metabase to register
a custom ASP.NET handler to service mydata requests. Source: " + url +
"
");
");
context.Response.Write(
"
context.Response.Write(HttpUtility.HtmlEncode(output));
context.Response.Write("");
}
// Override the IsReusable property.
public bool IsReusable
{
get { return true; }
}
// Private members.
private string RetrieveAspxSource(Uri u)
{
// Drop the handler string from the URL.
string handler = "/source.axd";
string url = u.ToString().Replace(handler, "");
UriBuilder builder = new UriBuilder(url);
// Get the server path to the ASPX file.
string file = HttpContext.Current.Server.MapPath(
builder.Uri.LocalPath);
// Read the source code.
StreamReader reader = new StreamReader(file);
string source = reader.ReadToEnd();
reader.Close();
return source;
}
}
Figure 3: The source code of SourceHandler to view the source of the specified ASPX file.
Figure 4: The source.axd handler in action.
To register the source handler with your application, enter the following script into the web.config file:
type="AspNetPro.SourceHandler,
SourceHandler"/> The SourceHandler
HTTP handler allows you to take a quick look at the structure of a given Web
page through the browser and in all those situations in which you don't have
Visual Studio.NET available (i.e. a test environment). This
idea is nothing really new, and several similar tools are available over the
Web; let me try to go one step further. What if the handler returns the source
code of the class that ASP.NET creates to service the request? The source code
of this file is left on the Web server when the application operates in debug
mode. (A feature you can toggle on and off through the web.config file.) Add
the following code to the configuration file and bind the handler to the
compiled.axd URL: type="AspNetPro.SourceHandler,
SourceHandler" /> ASP.NET
parses the source code of the requested ASPX resource, and stores the resulting
class in the directory pointed to by the following property: string tempDir
= HttpRuntime.CodegenDir; In this
folder, each requested page is accompanied by an XML file that contains the
name of the dynamic class and assembly. Using this information, you retrieve
the path of the dynamically created class and display its source code (see
Figure 5). Note that this source code is just the code that ASP.NET shows
whenever you get a compiler error. Using this handler, you can get it whenever
the debug mode is on. For more information, download the companion code. A
source file for the page must exist for the compiled.axd handler to work. If
you don't get it, point the browser to the page first (in ASP.NET debug mode)
and then retry with the handler. Finally, note that this code may not work in
ASP.NET 2.0. Conclusion Along
with HTTP modules, HTTP handlers are the building blocks of the ASP.NET
platform. Everything that occurs under the hood of the ASP.NET runtime
environment occurs because of HTTP handlers. When you invoke a Web page, or a
Web service method, an appropriate HTTP handler gets into the game and serves
your request. ASP.NET includes several predefined handlers, but you can write
handlers of your own to perform a variety of tasks and process any sort of
user-defined URL. The sample code in this
article is available for download. Dino
Esposito is a
Wintellect trainer and consultant who specializes in ASP.NET and ADO.NET.
Author of Programming Microsoft ASP.NET and Introducing ASP.NET
2.0, both from
Microsoft Press, Dino also helped several companies architect and build
effective products for ASP.NET developers. Dino is the cofounder of http://www.VB2TheMax.com, a popular portal
for VB and .NET programmers. Write to him at mailto:[email protected] or join
the blog at http://weblogs.asp.net/despos.
Figure 5: The compiled.axd handler in
action.