Once the context for the request is created, the HttpRuntime class sets up an ASP.NET application object to carry out the request. An ASP.NET application consists of an instance of the HttpApplication class that we briefly met in Chapter 3. HttpApplication is a global.asax-derived object that handles all HTTP requests directed to a particular virtual folder.
An ASP.NET running application is wholly represented by its virtual folder and, optionally, by the global.asax file. The virtual folder name is a sort of key that the HTTP runtime uses to selectively identify which of the running applications should take care of the incoming request. The global.asax file, if present, contains settings and code for responding to application-level events raised by ASP.NET or by registered HTTP modules that affect the application.
The particular HttpApplication class selected is responsible for managing the entire lifetime of the request it is assigned to. That instance of HttpApplication can be reused only after the request has been completed. If no HttpApplication object is available, either because the application has not been started yet or all valid objects are busy, a new HttpApplication is created and pooled.
Although the HttpApplication provides a public constructor, user applications never need to create instances of the HttpApplication class directly. The ASP.NET runtime infrastructure always does the job for you. As mentioned, instances of the class are pooled and, as such, can process many requests in their lifetime, but always one at a time. Should concurrent requests arrive for the same application, additional instances are created. Table 12-1 lists the properties defined for the HttpApplication class.
Property | Description |
---|---|
Application | Instance of the HttpApplicationState class. It represents the global and shared state of the application. It is functionally equivalent to the ASP intrinsic Application object. |
Context | Instance of the HttpContext class. It encapsulates in a single object all HTTP-specific information about the current request. Intrinsic objects (for example, Application and Request) are also exposed as properties. |
Modules | Gets the collection of modules that affect the current application. |
Request | Instance of the HttpRequest class. It represents the current HTTP request. It is functionally equivalent to the ASP intrinsic Request object. |
Response | Instance of the HttpResponse class. It sends HTTP response data to the client. It is functionally equivalent to the ASP intrinsic Response object. |
Server | Instance of the HttpServerUtility class. It provides helper methods for processing Web requests. It is functionally equivalent to the ASP intrinsic Server object. |
Session | Instance of the HttpSessionState class. It manages user-specific data. It is functionally equivalent to the ASP intrinsic Session object. |
User | An IPrincipal object that represents the user making the request. |
The HttpApplication is managed by the ASP.NET infrastructure, so how can you take advantage of the fairly rich, public programming interface of the class? The answer is that properties and, even more, overridable methods and class events can be accessed and programmatically manipulated in the global.asax file. (We'll return to global.asax in a moment.)
The property Modules returns a collection of application-wide components providing ad hoc services. An HTTP module component is a class that implements the IHttpModule interface. Modules can be considered the managed counterpart of ISAPI filters; they are a kind of request interceptor with the built-in capability of modifying the overall context of the request being processed. The Microsoft .NET Framework defines a number of standard modules, as listed in Table 12-2. Custom modules can be defined, too. I cover this particular aspect of HTTP programming in my other recent book, Programming Microsoft ASP.NET 2.0 Applications: Advanced Topics (Microsoft Press, 2005).
Module | Description |
---|---|
AnonymousIdentification | Assigns anonymous users a fake identity. Not installed in ASP.NET 1.x. |
FileAuthorization | Verifies that the remote user has Microsoft Windows NT permissions to access the requested resource. |
FormsAuthentication | Enables applications to use forms authentication. |
OutputCache | Provides page output caching services. |
PassportAuthentication | Provides a wrapper around Passport authentication services. |
Profile | Provides user profile services. Not installed in ASP.NET 1.x. |
RoleManager | Provides session-state services for the application. Not installed in ASP.NET 1.x. |
SessionState | Provides session-state services for the application. |
UrlAuthorization | Provides URL-based authorization services to access specified resources. |
WindowsAuthentication | Enables ASP.NET applications to use Windows and IIS-based authentication. |
The list of default modules is defined in the machine.config file. The modules listed in machine.config are available to all applications. By creating a proper web.config file, you can also create an application-specific list of modules. (Configuration is also covered in Programming Microsoft ASP.NET 2.0 Applications: Advanced Topics.)
The methods of the HttpApplication class can be divided in two groups operational methods and event handler managers. The HttpApplication operational methods are described in Table 12-3.
Method | Description |
---|---|
CompleteRequest | Sets an internal flag that causes ASP.NET to skip all successive steps in the pipeline and directly execute EndRequest. Mostly useful to HTTP modules. |
Dispose | Overridable method, cleans up the instance variables of all registered modules once the request has been served. At this time, Request, Response, Session, and Application are no longer available. |
GetVaryByCustomString | Overridable method, provides a way to set output caching based on a custom string for all pages in the application. (We'll say more about output page caching in Chapter 14.) |
Init | Overridable method that executes custom initialization code after all modules have been linked to the application to serve the request. You can use it to create and configure any object that you want to use throughout the request processing. At this time, Request, Response, Session, and Application are not yet available. |
Note that Init and Dispose methods are quite different from well-known event handlers such as Application_Start and Application_End.
Init executes for every request directed at the Web application, whereas Application_Start fires only once in the Web application's lifetime. Init indicates that a new instance of the HttpApplication class has been created to serve an incoming request; Application_Start denotes that the first instance of the HttpApplication class has been created to start up the Web application and serve its very first request. Likewise, Dispose signals the next termination of the request processing but not necessarily the end of the application. Application_End is raised only once, when the application is being shut down.
Note | The lifetime of any resources created in the Init method is limited to the execution of the current request. Any resource you allocate in Init should be disposed of in Dispose, at the latest. If you need persistent data, resort to other objects that form the application or session state. You can find more information on these objects in Chapter 13 and Chapter 14. |
In addition to the operational methods in Table 12-3, a few other HttpApplication methods are available to register asynchronous handlers for application-level events. These methods are of little interest to user applications and are used only by HTTP modules to hook up the events generated during the request's chain of execution.
Table 12-4 describes the event model of the HttpApplication class that is, the set of events that HTTP modules, as well as user applications, can listen to and handle.
Event | Description |
---|---|
AcquireRequestState, PostAcquireRequestState | Occurs when the handler that will actually serve the request acquires the state information associated with the request. The post event is not available in ASP.NET 1.x. |
AuthenticateRequest, PostAuthenticateRequest | Occurs when a security module has established the identity of the user. The post event is not available in ASP.NET 1.x. |
AuthorizeRequest, PostAuthorizeRequest | Occurs when a security module has verified user authorization. The post event is not available in ASP.NET 1.x. |
BeginRequest | Occurs as soon as the HTTP pipeline begins to process the request. |
Disposed | Occurs when the HttpApplication object is disposed of as a result of a call to Dispose. |
EndRequest | Occurs as the last event in the HTTP pipeline chain of execution. |
Error | Occurs when an unhandled exception is thrown. |
PostMapRequestHandler | Occurs when the HTTP handler to serve the request has been found. The event is not available in ASP.NET 1.x. |
PostRequestHandlerExecute | Occurs when the HTTP handler of choice finishes execution. The response text has been generated at this point. |
PreRequestHandlerExecute | Occurs just before the HTTP handler of choice begins to work. |
PreSendRequestContent | Occurs just before the ASP.NET runtime sends the response text to the client. |
PreSendRequestHeaders | Occurs just before the ASP.NET runtime sends HTTP headers to the client. |
ReleaseRequestState, PostReleaseRequestState | Occurs when the handler releases the state information associated with the current request. The post event is not available in ASP.NET 1.x. |
ResolveRequestCache, PostResolveRequestCache | Occurs when the ASP.NET runtime resolves the request through the output cache. The post event is not available in ASP.NET 1.x. |
UpdateRequestCache, PostUpdateRequestCache | Occurs when the ASP.NET runtime stores the response of the current request in the output cache to be used to serve subsequent requests. The post event is not available in ASP.NET 1.x. |
To handle any of these events asynchronously, an application will use the corresponding method whose name follows a common pattern: AddOnXXXAsync, where XXX stands for the event name. To hook up some of these events in a synchronous manner, an application will define in global.asax event handler procedures with the following signature:
public void Application_XXX(object sender, EventArgs e) { // Do something here }
Of course, the XXX placeholder must be replaced with the name of the event. All the events in the preceding table provide no event-specific data. You could also use the following simpler syntax without losing additional information and programming power:
public void Application_XXX() { // Do something here }
In addition to the events listed in Table 12-4, in global.asax an application can also handle Application_Start and Application_End. When ASP.NET is about to fire BeginRequest for the very first time in the application lifetime, it makes Application_Start precede it. EndRequest will happen at the end of every request to an application. Application_End occurs outside the context of a request, when the application is ending.
Application events are fired in the following sequence:
BeginRequest. The ASP.NET HTTP pipeline begins to work on the request. This event reaches the application after Application_Start.
AuthenticateRequest. The request is being authenticated. All the internal ASP.NET authentication modules subscribe to this event and attempt to produce an identity. If no authentication module produced an authenticated user, an internal default authentication module is invoked to produce an identity for the unauthenticated user. This is done for the sake of consistency so that code doesn't need to worry about null identities.
PostAuthenticateRequest. The request has been authenticated. All the information available is stored in the HttpContext's User property.
AuthorizeRequest. The request authorization is about to occur. This event is commonly handled by application code to do custom authorization based on business logic or other application requirements.
PostAuthorizeRequest. The request has been authorized.
ResolveRequestCache. The ASP.NET runtime verifies whether returning a previously cached page can resolve the request. If a valid cached representation is found, the request is served from the cache and the request is short-circuited, calling only any registered EndRequest handlers.
PostResolveRequestCache. The request can't be served from the cache, and the procedure continues. An HTTP handler corresponding to the requested URL is created at this point. If the requested resource is an .aspx page, an instance of a page class is created.
PostMapRequestHandler. The event fires when the HTTP handler corresponding to the requested URL has been successfully created.
AcquireRequestState. The module that hooks up this event is willing to retrieve any state information for the request. A number of factors are relevant here: the handler must support session state in some form, and there must be a valid session ID.
PostAcquireRequestState. The state information (such as Application or Session) has been acquired.
PreRequestHandlerExecute. This event is fired immediately prior to executing the handler for a given request. The handler does its job and generates the output for the client.
PostRequestHandlerExecute. This event is raised when the handler has generated the response text.
ReleaseRequestState. This event is raised when the handler releases its state information and prepares to shut down. This event is used by the session state module to update the dirty session state if necessary.
PostReleaseRequestState. The state, as modified by the page execution, has been persisted. Any relevant response filtering is done at this point. (I'll say more about this topic later.)
UpdateRequestCache. The ASP.NET runtime determines whether the generated output, now also properly filtered by registered modules, should be cached to be reused with upcoming identical requests.
PostUpdateRequestCache. The page has been saved to the output cache if it was configured to do so.
EndRequest. This event fires as the final step of the HTTP pipeline. The control passes back to the HttpRuntime object, which is responsible for the actual forwarding of the response to the client. At this point, the text has not been sent yet.
Another pair of events can occur during the request, but in a nondeterministic order: PreSendRequestHeaders and PreSendRequestContent. The PreSendRequestHeaders event informs the HttpApplication object in charge of the request that HTTP headers are about to be sent. The event normally fires after EndRequest but not always. For example, if buffering is turned off, the event gets fired as soon as some content is going to be sent to the client. Finally, with the PreSendRequestContent event, the HttpApplication object in charge of the request learns that the response body is about to be sent. Speaking of nondeterministic application events, it must be said that a third nondeterministic event is, of course, Error.