Microsoft Press
Books designed for the different ways you learn. And across the range of Microsoft technologies. Welcome!
Hi, everyone. Dino Esposito’s newest book, Programming Microsoft ASP.NET 4 (ISBN 9780735643383; 992 pages), is now available. Take a look at this earlier post to read the book’s Introduction, which describes the book’s contents and intended audience.
Today we’d like to share a chapter excerpt from the book. Enjoy.
Chapter 4
HTTP Handlers, Modules, and Routing
Advice is what we ask for when we already know the answer but wish we didn’t. —Erica Jong
HTTP handlers and modules are truly the building blocks of the ASP.NET platform. Any requests for a resource managed by ASP.NET are always resolved by an HTTP handler and pass through a pipeline of HTTP modules. After the handler has processed the request, the request flows back through the pipeline of HTTP modules and is finally transformed into markup for the caller.
The Page class—the base class for all ASP.NET runtime pages—is ultimately an HTTP handler that implements internally the page life cycle that fi res the well-known set of page events, including postbacks, Init, Load, PreRender, and the like. An HTTP handler is designed to pro- cess one or more URL extensions. Handlers can be given an application or machine scope, which means they can process the assigned extensions within the context of the current application or all applications installed on the machine. Of course, this is accomplished by making changes to either the site’s web.config file or a local web.config file, depending on the scope you desire.
HTTP modules are classes that handle runtime events. There are two types of public events that a module can deal with. They are the events raised by HttpApplication (including asyn- chronous events) and events raised by other HTTP modules. For example, SessionStateModule is one of the built-in modules provided by ASP.NET to supply session-state services to an application. It fi res the End and Start events that other modules can handle through the familiar Session_End and Session_Start signatures.
In Internet Information Services (IIS) 7 integrated mode, modules and handlers are resolved at the IIS level; they operate, instead, inside the ASP.NET worker process in different runtime configurations, such as IIS 7 classic mode or IIS 6.
HTTP modules and handlers are related to the theme of request routing. Originally developed for ASP.NET MVC, the URL routing engine has been incorporated into the over- all ASP.NET platform with the .NET Framework 3.5 Service Pack 1. The URL routing engine is a system-provided HTTP module that hooks up any incoming requests and attempts to match the requested URL to one of the user-defined rewriting rules (known as routes). If a match exists, the module locates the HTTP handler that is due to serve the route and goes with it. If no match is found, the request is processed as usual in Web Forms, as if no URL routing engine was ever in the middle. What makes the URL routing engine so beneficial to applications? It actually enables you to use free-hand and easy-to-remember URLs that are not necessarily bound to physical fi les in the Web server.
In this chapter, we’ll explore the syntax and semantics of HTTP handlers, HTTP modules, and the URL routing engine.
The ISAPI Extensibility Model of IIS
A Web server generally provides an application programming interface (API) for enhancing and customizing the server’s capabilities. Historically speaking, the first of these extension APIs was the Common Gateway Interface (CGI). A CGI module is a new application that is spawned from the Web server to service a request. Nowadays, CGI applications are almost never used because they require a new process for each HTTP request, and this approach poses severe scalability issues and is rather inadequate for high-volume Web sites.
More recent versions of Web servers supply an alternate and more efficient model to extend the capabilities of the server. In IIS, this alternative model takes the form of the ISAPI interface. When the ISAPI model is used, instead of starting a new process for each request, the Web server loads a made-to-measure component—namely, a Win32 dynamic-link library (DLL)—into its own process. Next, it calls a well-known entry point on the DLL to serve the request. The ISAPI component stays loaded until IIS is shut down and can service requests without any further impact on Web server activ- ity. The downside to such a model is that because components are loaded within the Web server process, a single faulty component can tear down the whole server and all installed applications. Some effective countermeasures have been taken over the years to smooth out this problem. Today, IIS installed applications are assigned to application pools and each application pool is served by a distinct instance of a worker process.
From an extensibility standpoint, however, the ISAPI model is less than optimal because it requires developers to create Win32 unmanaged DLLs to endow the Web server with the capability of serving specific requests, such as those for ASPX resources. Until IIS 7 (and still in IIS 7 when the classic mode is configured), requests are processed by IIS and then mapped to some ISAPI (unmanaged) component. This is exactly what happens with plain ASPX requests, and the ASP.NET ISAPI component is aspnet_isapi.dll. In IIS 7.x integrated mode, you can add managed components (HTTP handlers and HTTP mod- ules) directly at the IIS level. More precisely, the IIS 7 integrated mode merges the ASP.NET internal runtime pipeline with the IIS pipeline and enables you to write Web server extensions using managed code. This is the way to go.
Today, if you learn how to write HTTP handlers and HTTP modules, you can use such skills to customize how any requests that hit IIS are served, and not just requests that would be mapped to ASP.NET. You’ll see a few examples in the rest of the chapter.
Writing HTTP Handlers
As the name suggests, an HTTP handler is a component that handles and processes a request. ASP.NET comes with a set of built-in handlers to accommodate a number of system tasks. The model, however, is highly extensible. You can write a custom HTTP handler when- ever you need ASP.NET to process certain types of requests in a nonstandard way. The list of useful things you can do with HTTP handlers is limited only by your imagination.
Through a well-written handler, you can have your users invoke any sort of functionality via the Web. For example, you could implement click counters and any sort of image manipula- tion, including dynamic generation of images, server-side caching, or obstructing undesired linking to your images. More in general, an HTTP handler is a way for the user to send a command to the Web application instead of just requesting a particular page.
In software terms, an HTTP handler is a relatively simple class that implements the IHttpHandler interface. An HTTP handler can either work synchronously or operate in an asynchronous way. When working synchronously, a handler doesn’t return until it’s done with the HTTP request. An asynchronous handler, on the other hand, launches a potentially lengthy process and returns immediately after. A typical implementation of asynchronous handlers is asynchronous pages. An asynchronous HTTP handler is a class that implements a different interface—the IHttpAsyncHandler interface.
HTTP handlers need be registered with the application. You do that in the application’s web. config file in the <httpHandlers> section of <system.web>, in the <handlers> section of <system.webServer> as explained in Chapter 3, “ASP.NET Configuration,” or in both places. If your application runs under IIS 7.x in integrated mode, you can also configure HTTP handlers via the Handler Mappings panel of the IIS Manager.
The IHttpHandler Interface
Want to take the splash and dive into HTTP handler programming? Well, your first step is getting the hang of the IHttpHandler interface. An HTTP handler is just a managed class that implements that interface. As mentioned, a synchronous HTTP handler implements the IHttpHandler interface; an asynchronous HTTP handler, on the other hand, implements the IHttpAsyncHandler interface. Let’s tackle synchronous handlers first.
The contract of the IHttpHandler interface defines the actions that a handler needs to take to process an HTTP request synchronously.
Members of the IHttpHandler Interface
The IHttpHandler interface defines only two members: ProcessRequest and IsReusable, as shown in Table 4-1. ProcessRequest is a method, whereas IsReusable is a Boolean property.
The IsReusable property on the System.Web.UI.Page class—the most common HTTP handler in ASP.NET—returns false, meaning that a new instance of the HTTP request is needed to serve each new page request. You typically make IsReusable return false in all situations where some significant processing is required that depends on the request payload. Handlers used as simple barriers to filter special requests can set IsReusable to true to save some CPU cycles. I’ll return to this subject with a concrete example in a moment.
The ProcessRequest method has the following signature:
void ProcessRequest(HttpContext context);
It takes the context of the request as the input and ensures that the request is serviced. In the case of synchronous handlers, when ProcessRequest returns, the output is ready for forwarding to the client.
A Very Simple HTTP Handler
The output for the request is built within the ProcessRequest method, as shown in the following code:
using System.Web; namespace AspNetGallery.Extensions.Handlers { public class SimpleHandler : IHttpHandler { public void ProcessRequest(HttpContext context) { const String htmlTemplate = "<html><head><title>{0}</title></head><body>" + "<h1>Hello I'm: " + "<span style='color:blue'>{1}</span></h1>' + "</body></html>"; var response = String.Format(htmlTemplate, "HTTP Handlers", context.Request.Path); context.Response.Write(response); } public Boolean IsReusable { get { return false; } } } }
You need an entry point to be able to call the handler. In this context, an entry point into the handler’s code is nothing more than an HTTP endpoint—that is, a public URL. The URL must be a unique name that IIS and the ASP.NET runtime can map to this code. When registered, the mapping between an HTTP handler and a Web server resource is established through the web.config file:
<configuration> <system.web> <httpHandlers> <add verb="*" path="hello.axd" type="Samples.Components.SimpleHandler" /> </httpHandlers> </system.web> <system.webServer> <validation validateIntegratedModeConfiguration="false" /> <handlers> <add name="Hello" preCondition="integratedMode" verb="*" path="hello.axd" type="Samples.Components.SimpleHandler" /> </handlers> </system.webServer> </configuration>
The <httpHandlers> section lists the handlers available for the current application. These settings indicate that SimpleHandler is in charge of handling any incoming requests for an endpoint named hello.axd. Note that the URL hello.axd doesn’t have to be a physical resource on the server; it’s simply a public resource identifier. The type attribute references the class and assembly that contain the handler. Its canonical format is type[,assembly]. You omit the assembly information if the component is defined in the App_Code or other reserved folders.
Important As noted in Chapter 3, you usually don’t need both forms of an HTTP handler declaration in <system.web> and <system.webServer>. You need the former only if your applica- tion runs under IIS 6 (Windows Server 2003) or if it runs under IIS 7.x but is configured in classic mode. You need the latter only if your application runs under IIS 7.x in integrated mode. If you have both sections, you enable yourself to use a single web.config file for two distinct deploy- ment scenarios. In this case, the <validation> element is key because it prevents IIS 7.x from strictly parsing the content of the configuration file. Furthermore, as discussed in Chapter 3, the <httpHandlers> and <httpModules> sections help in testing handlers and modules within Visual Studio if you’re using the embedded ASP.NET Development Server (also known as, Cassini).
If you invoke the hello.axd URL, you obtain the results shown in Figure 4-1.
The technique discussed here is the quickest and simplest way of putting an HTTP handler to work, but there is more to know about the registration of HTTP handlers and there are many more options to take advantage of.
Note It’s more common to use the ASHX extension for a handler mapping. The AXD extension is generally reserved for resource handlers that inject embedded content such as images, scripts, and so forth.
[end of excerpt]
Here is the chapter’s complete head structure, to give you a sense of its full coverage:
Members of the IHttpHandler Interface A Very Simple HTTP Handler
Registering the Handler Preconditions for Managed Handlers Handlers Serving New Types of Resources
Designing the HTTP Handler
Implementing the HTTP Handler
Loading Images from Databases Serving Dynamically Generated Images Writing Copyright Notes on Images
Controlling Images via an HTTP Handler
Deploying Handlers as ASHX Resources Prevent Access to Forbidden Resources Should It Be Reusable or Not? HTTP Handler Factories Asynchronous Handlers Implementing Asynchronous Handlers
Wiring Up Events
Registering with the Configuration File Accessing Other HTTP Modules
The UrlRoutingModule Class
The PostResolveRequestCache Event
Original URL Rewriting API URL Patterns and Routes
Defining Routes for Specific Pages Programmatic Access to Route Values Structure of Routes Preventing Routing for Defined URLs