ASP.NET

The ASP.NET Pipeline

As soon as ASP.NET 1.0 was released, it offered a huge improvement over classic ASP by introducing well-defined pipelines. Classic ASP was patched together from several disparate components (IIS, the Web Application Manager, and the ASP ISAPI DLL). The Request and Response objects were COM objects hanging off the threads owned by IIS. If you wanted to do any processing outside the context of ASP, you needed to write an ISAPI filter. If you wanted to write code to execute during processing, it had to occur within a COM object implementing IDispatch (severely limiting the available types of data you could use and negatively affecting performance). If you wanted to write any request-handling code (outside the context of ASP), you had to write a separate ISAPI DLL. The HTTP pipeline includes the facilities to do these things, but in a much more manageable way.

In ASP.NET, your application has the opportunity to perform preprocessing and post processing within HttpModules. Your application also has the opportunity to process application-wide events using the HttpApplication object. Because of ASP.NET's object model, the need for separate scripting objects disappears. The endpoint of all requests is an implementation of IHttpHandler. ASP.NET already includes some useful implementations of IHttpHandler (that is, System.Web.UI.Page and System.Web.Services.WebService). However, you may easily write your own (as we'll see later).

Request Path

Once a request comes into the AppDomain managed by the ASP.NET runtime, ASP.NET uses the HttpWorkerRequest class to store the request information. Following that, the runtime wraps the request's information in a class named HttpContext. The HttpContext class includes all the information you'd ever want to know about a request, including references to the current request's HttpRequest and HttpResponse objects. The runtime produces an instance of HttpApplication (if one is not already available) and then fires a number of application-wide events (such as BeginRequest and AuthenticateRequest). These events are also pumped through any HttpModules attached to the pipeline. Finally, ASP.NET figures out what kind of handler is required to handle the request, creates one, and asks the handler to process the request. After the handler deals with the request, ASP.NET fires a number of post-processing events (like EndRequest) through the HttpApplication object and the HttpModules.

The following figure illustrates the structure of the ASP.NET pipeline inside the ASP.NET worker process:

Figure 2-4 Main components of the HTTP pipeline within ASP.NET.
Figure 2-4 Main components of the HTTP pipeline within ASP.NET.

While some of the parts within the pipeline are unavailable to you as a developer, several parts are available directly and provide a useful means of managing your request as it goes through the pipeline. The most important parts of the pipeline that you can touch include the HttpApplication, the HttpContext, the HttpModule, and the HttpHandler.

Following are some details about these critical sections within the HTTP request path.

The HttpApplication

At this point, you understand the nature of a Web application as being very different from that of a normal desktop application. The code that you're writing is responsible for spitting some HTML response back to a client. In many ways, the model hearkens back to the terminal-mainframe model prevalent during the mid-1970s. In ASP.NET, the endpoint of a request is an implementation of IHttpHandler.

HTTP handlers live for a very short period of time. They stick around long enough to handle a request, and then they disappear. For very simple applications, this model might be just fine. However, imagine the requirements of even a modest commercial-grade application. If all you had to work with was these ephemeral handlers, you'd have no way to achieve application-wide functionality. For example, imagine you wanted to cache data to avoid round-trips to the database. You'd need to store that data in a place where all the HTTP handlers could get to it.

The HttpApplication class exists for that purpose-to act as a rendezvous point for your request processing. During the lifetime of a Web application, the HttpApplication objects serve as places to hold application-wide data and handle application-side events.

The HttpContext

The HttpContext class acts a central location in which you can access parts of the current request as it travels through the pipeline. In fact, every aspect of the current request is available through HttpContext. Even though the HttpContext components are really just references to other parts of the pipeline, having them available in a single place makes it much easier to manage the request.

Here is an abbreviated listing of HttpContext, showing the parts you'll be using most frequently in developing Web applications. The members are exposed as properties.

class HttpContext
{
   public static HttpContext Current…;
   public HttpRequest Request…;
   public HttpResponse Response…;
   public HttpSessionState Session…;
   public HttpServerUtility Server…;
   public Application HttpApplicationState…;
   public ApplicationInstance HttpApplication…;
   public IDictionary Items…;
   public IPrincipal User…;
   public IHttpHandler CurrentHandler…;
   public Cache Cache…;
   …
}

The static Current property gives you a means of getting to the current request at any time. Many times, the HttpContext is passed as a method parameter (as in the method IHttpHandler.RequestProcess(HttpContext ctx); however, there may be times when you need the context even though it hasn't been passed as a parameter. The Current property lets you grab the current process out of thin air. For example, this is how you might use HttpContext.Current:

Public void DealWithRequest()
{
   HttpContext thisRequest = HttpContext.Current;
   thisRequest.Response.Write("<h3> Hello World</h3>");
}

The other properties within HttpContext include such nuggets as

  • a reference to the context's Response object (so you can send output to the client)

  • a reference to the Request object (so you can find information about the request itself)

  • a reference to the central application itself (so you can get to the application state)

  • a reference to a per-request dictionary (for storing items for the duration of a request)

  • a reference to the application-wide cache (to store data and avoid round-trips to the database)

We'll be seeing a lot more of the context-especially within the context of a custom HttpHandler.

HttpModules

While the Application object is suitable for handling application-wide events and data on a small scale, sometimes application-wide tasks need a little heavier machinery. HttpModules serve that purpose.

ASP.NET includes a number of predefined HttpModules. For example, session state, authentication, and authorization are handled via HttpModules. Writing HttpModules is pretty straightforward, and is a great way to handle complex application-wide operations. For example, if you wanted to write your own authentication scheme, using HTTPModules is a good way to do it. We'll see HTTPModules up close later.

HttpHandlers

The last stop a request makes in the pipeline is an HttpHandler. Any class implementing the interface IHttpHandler qualifies as a handler. When a request finally reaches the end of the pipeline, ASP.NET consults the configuration file to see if the particular file extension is mapped to an HttpHandler. If it is, the ASP.NET loads the handler and calls the handler's IHttpHandler.ProcessRequest method to execute the request.

ASP.NET includes several HTTPHandlers already, including System.Web.UI.Page and System.Web.Services.WebService. We'll use these as we move forward. In addition, we'll also learn how to write an HTTPHandler completely from scratch.