Thursday, March 12, 2015

Understanding Handlers in the Request Life Cycle

A common source of confusion when working with the ASP.NET platform is the difference between modules and handlers, so it is important I explain the role of each component before digging into the details of how handlers work.
The best way to understand each component is within the scope of the request life cycle. As I explained in Chapter 4, modules have two roles in request handling. They can be used flor diagnosis and debugging, or they can provide services used by other components. The modules I created in Chapter 4 fall into the diagnosis category: They added fragments of HTML to the response that provided insight into how the request was processed. By contrast, most of the built-in modules that come with ASP.NET provide services, such as caching, security, or the management of state data. As I explained in Chapter 1, these are the services that you consume within MVC framework controllers and views, as shown in Figure 5-2.
Click to collapse 
Figure 5-2: The relationship between module services and MVC framework controllers
This figure is a refinement of one I showed you in Chapter 1, expanded to show more details about the ASP. NET components and request life cycle. As the figure illustrates, modules are instantiated at the start of the request handling process, and they set up services that are consumed through the ASP.NET context objects by the MVC framework controller and view in order to generate the response that will be sent to the client.
In Figure 5-1, I have shown the MVC framework programmer's view of the controller and view, but, as you may have guessed, the MVC functionality is integrated into the ASP.NET platform through a handler, as shown in Figure 5-3.
Click to collapse 
Figure 5-3: Showing the handler in the request life cycle
The first thing to notice is that there are multiple modules for each request but only one handler. The reason I am being so emphatic about the relationship between these components is because I want to be clear about they mesh with the request life-cycle events: The modules are created as soon as the request life cycle begins, but the selection and creation of the handler are built right into the life cycle, as shown in Figure 5-4.
Click to collapse 
Figure 5-4: The relationship between life-cycle events, modules, and handlers
The MapRequestHandler event is triggered before the ASP.NET platform locates the handler that will generate the content for the request, the process for which I describe in the "Creating a Handler" section later in this chapter. The PostMapRequestHandler event is triggered once the handler has been identified and instantiated. However, the handler isn't asked to generate the content until the PreRequestHandlerExecute event is triggered, which means that modules have an opportunity to respond to the handler selection and provide services that are unique to that handler, as shown in the figure. You'll see how this all works in the "Targeting a Specific Handler" section later in this chapter. (Modules can also override the handler selection process, as described in Chapter 6.)

Understanding Handlers

Handlers are classes that implement the System.Web.IHttpHandler interface, which defines the two methods that I have described in Table 5-3.

Table 5-3: The Members Defined by the IHttpHandler Interface 
 Open table as spreadsheet
Name
Description
ProcessRequest (context)
This method is called when the ASP.NET framework wants the handler to generate a response for a request. The parameter is an HttpContext object, which provides access to details of the request.
IsReusable
This property tells the ASP.NET framework whether the handler can be used to handle further requests. If the property returns false, then the ASP.NET framework will create new instances for each request. In most situations, returning a value of true doesn't mean that handlers will be reused unless you implement a custom handler factory, which I describe in the "Custom Handler Factories" section of this chapter.

The ProcessRequest method is passed an HttpContext object, which can be used to inspect the request and the state of the application through the properties I described in Chapter 3.
Handlers can generate any kind of content that can be delivered over HTTP, and the ASP.NET platform does not impose any constraints on how the content is created. ASP.NET includes a set of default handlers that support the Web Forms, the MVC framework, SignalR, and Web API technology stacks, but custom handlers can be used to support new kinds of applications or data.

Handlers and the Life-Cycle Events

In Figure 5-3, I explained that handler selection and content generation are part of the request life cycle, but I describe the significant events from the perspective of modules. Table 5-4 describes the key life-cycle events from the perspective of the handler.

Table 5-4: The Request Life-Cycle Events Relevant to Handlers 
 Open table as spreadsheet
Name
Description
MapRequestHandler
PostMapRequestHandler
MapRequestHandler is triggered when the ASP.NET framework wants to locate a handler for the request. A new instance of the handler will be created unless an existing instance declares it can be reused. The PostMapRequestHandler event is triggered once the handler has been selected.
PreRequestHandlerExecute
PostRequestHandlerExecute
These events are triggered immediately before and after the call to the handler ProcessRequest method.

The life cycle of a handler is interwoven with the request and module life cycles. This may seem over complicated, but it provides for flexible interactions between handlers and modules (or the global application class if that's where you have defined your event handlers). All of this will start to make more sense as you see some examples of handlers and the way they can be used.
The MapRequestHandler and PostMapRequestHandler events are different from the other pairs of events in the life cycle. Normally, the first event in a pair is a request for a module to provide a service, and the second event signals that phase of the life cycle is complete. So, for example, the AcquireRequestState event is a request for modules that handle state data to associate data with the request, and thePostAcquireRequestState event signals that all of the modules that handled the first event have finished responding.
The MapRequestHandler event isn't an invitation for a module to supply a handler for a request; that's a task that ASP.NET handles itself, and the event just signals that the selection is about to be made (a process I describe in the next section). The PostMapRequestHandler event signals that the handler has been selected, which allows modules to respond to the handler choice—generally by setting up services or data specific to the chosen handler.
The handler's ProcessRequest method is called between the PreRequestHandlerExecute and PostRequestHandlerExecute events. Modules can use these events as the last opportunity to manipulate the context objects before the content is generated by the handler and the first opportunity to manipulate the response once the handler is done.

Friday, March 6, 2015

The ASP.NET Context Objects

Obtaining an HttpContext in Different ASP.NET/MVC Components
Component
Technique
Controller
Use the HttpContext property defined by Controller, which is the base class for MVC framework controllers.
View
Use the Context property defined by WebViewPage, which is the base class used to compile Razor views.
Global Application Class
Use the Context convenience property defined by the HttpApplication class (which is the base for the global application class).
Module
The Init method is passed an HttpContext object when it is invoked, and the life-cycle event handlers are passed an HttpApplication object, which defines a Context property. SeeChapter 4 for details of modules.
Handler
The ProcessRequest method is passed an HttpContext object when it is invoked. See Chapter 5 for details of handlers.
Universally
You can always get the HttpContext object associated with the current request through the static HttpContext.Current property.

Working with HttpApplication Objects

Most of these members are convenience properties that map to the properties of the HttpContext class, but there are some points to note, as discussed in the following section.

Handling Property Exceptions

The Request, Response, Session, and User properties all return the value of the corresponding properties from the HttpContext class, but with a wrinkle—all of these properties will throw an HttpException if the value they get from HttpContext is null.
This happens because the HttpApplication class receives notifications for two different life cycles: the application life cycle and the request life cycle. Objects that describe a single request are not available when an instance of the global application class is being used to handle application-level events, so the HttpException is thrown if you try to access request-related properties when handling application-level notifications.
The policy of throwing an exception is harsh because it makes it hard to deal with HttpApplication objects of unknown provenance. You can see an example of this issue in Listing 3-9, which shows changes to the global application class.

Listing 3-9: Writing Code that Deals with Both Kinds of HttpApplication Objects in the Global.asax.cs File

using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;
using System.Web.Routing;

namespace SimpleApp {
    public class MvcApplication : System.Web.HttpApplication {

        public MvcApplication() {
            PostAcquireRequestState += (src, args) => CreateTimeStamp();
        }

        protected void Application_Start() {
            AreaRegistration.RegisterAllAreas();
            RouteConfig.RegisterRoutes(RouteTable.Routes);
            CreateTimeStamp();
        }

        private void CreateTimeStamp() {
            string stamp = Context.Timestamp.ToLongTimeString();
            if (Session != null) {
                Session["request_timestamp"] = stamp;
            } else {
                Application["app_timestamp"] = stamp;
            }
        }
    }
}

I have removed the code that registers and handles some of the request life-cycle events and have defined a new method that creates a timestamp and stores it as state data. I describe the ASP.NET framework support for state data (including the objects returned by Session and Application properties used here) in Chapter 10, but for now it is enough to know that the Session property will return an object for storing state for individual requests.

Tip 
Notice that I create the request timestamp in response to the PostAcquireRequestState event. This is because the modules that provide state data services for requests are not asked to do so until theAcquireRequestState event; therefore, the Session property will return null, even if the global application class instance has been created to process events for a request. Don't worry if this doesn't make sense now; I explain how modules work in Chapter 4 and describe the different types of state data in Chapter 10.
The new method, called CreateTimeStamp, aims to reduce code duplication by dealing with application-level and request-level timestamps with the same set of statements. This code will throw an exception as soon as the application is started because it attempts to read the Session property for an instance of the global application class that has been created to deal with the Application_Start method being called.

I could address this by using try…catch blocks, but that would be bad practice because exception handling should not be used to manage the regular and expected flow of an application. Instead, I can use the equivalent properties directly on HttpContext, as shown in Listing 3-10. This allows me to use one method to create both kinds of timestamps in a single method without having to worry about exceptions.
Listing 3-10: Using the Properties Defined by the HttpContext Class in the Global.asax.cs File

…
private void CreateTimeStamp() {
    string stamp = Context.Timestamp.ToLongTimeString();
    if (Context.Session != null) {
        Session["request_timestamp"] = stamp;
    } else {
        Application["app_timestamp"] = stamp;
    }
}



Caution 
The change in Listing 3-10 won't work until you have also applied the changes in Listing 3-11.
Notice that I only have to change the initial check for the Session property; if it isn't null, I can use the Session property defined by the HttpApplication class. The Application property will always return anHttpApplicationState object because, as I explain in Chapter 10, the state management feature it provides is application-wide and is initialized when the application is started.
You need to pay attention to this issue only when working directly with the global application class where you can be working with objects that are not associated with an individual request. Elsewhere in ASP.NET, and especially within MVC framework controller classes, you will always be working with context objects that represent a request. As an example, Listing 3-11 shows the changes I made to the HomeController class to display the timestamps created in the previous listing.
Listing 3-11: Displaying Timestamps in the HomeController.cs File


using System.Collections.Generic;
using System.Web.Mvc;
using SimpleApp.Models;

namespace SimpleApp.Controllers {
    public class HomeController : Controller {

        public ActionResult Index() {
            return View(GetTimeStamps());
        }

        [HttpPost]
        public ActionResult Index(Color color) {
            Color? oldColor = Session["color"] as Color?;
            if (oldColor != null) {
                Votes.ChangeVote(color, (Color)oldColor);
            } else {
                Votes.RecordVote(color);
            }
            ViewBag.SelectedColor = Session["color"] = color;
            return View(GetTimeStamps());
        }

        private List GetTimeStamps() {
            return new List {
                string.Format("App timestamp: {0}",
                    HttpContext.Application["app_timestamp"]),
                string.Format("Request timestamp: {0}", Session["request_timestamp"]),
            };
        }
    }
}

I have used the C# string composition feature, available through the static String.Format method, to create a list of messages that will be passed to the view. The Controller class that is the base for MVC framework controllers provides a Session convenience property that corresponds to HttpContext.Session (but I have to access HttpContext.Application directly since there is no corresponding convenience property).
You can see the timestamps by starting the application, as illustrated by Figure 3-7. Both values will be the same initially, but if you reload the browser window a few times, you will see that the application timestamp remains constant while the request timestamp is updated each time.
Image from book 
Figure 3-7: Displaying application and request timestamps

Note 
If you find that both timestamps update for every request, then you may have forgotten to disable the Visual Studio Browser Link feature as described in Chapter 1. The Browser Link feature relies on injecting JavaScript code into the HTML sent to the browser that establishes a connection back to the server and waits for a change notification. The global application class handles all requests and not just those intended for the MVC framework, so the GetTimeStamps method is called twice for each request. Browser Link uses a feature called sessionless controllers that prevents the Context.Session property from ever being set, which has the effect of confusing the code in Listing 3-10 and updating the application-level timestamp. I describe sessionless controllers in Chapter 10.

Working with HttpRequest Objects


The HttpRequest object describes a single HTTP request as it is being processed. Table 3-12 describes the HttpRequest properties that provide information about the current request. (The HttpRequest class defines methods and some other properties, but I describe these in later chapters in the context of the platform features they correspond to.)


Table 3-12: The Descriptive Properties Defined by the HttpRequest Class 
 Open table as spreadsheet
Name
Description
AcceptTypes
Returns a string array containing the MIME types accepted by the browser.
Browser
Returns an HttpBrowserCapabilities object that describes the capabilities of the browser; see Chapter 7 for more details.
ContentEncoding
Returns a System.Text.Encoding object that represents the character set used to encode the request data.
ContentLength
Returns the number of bytes of content in the request.
ContentType
Returns the MIME type of the content included in the request.
CurrentExecutionFilePathExtension
Returns the file extension component of the requested URL.
Headers
Returns a collection containing the request headers.
HttpMethod
Returns the HTTP method used to make the request (GET, POST, and so on).
InputStream
Returns a stream that can be used to read the contents of the request.
IsLocal
Returns true when the request has originated from the local machine.
MapPath(path)
Translates a file name within the project to an absolute path. See Chapter 11 for an example.
RawUrl
Returns the part of the URL that follows the hostname. In other words, for http://apress.com:80/books/Default.aspx, this property would return /books/Default.aspx.
RequestContext
Returns a RequestContext object that provides access to the routing information for a request. I demonstrate the use of the RequestContext object in Chapter 6.
Url
Returns the request URL as a System.Uri object.
UrlReferrer
Returns the referrer URL as a System.Uri object.
UserAgent
Returns the user-agent string supplied by the browser.
UserHostAddress
Returns the IP address of the remote client, expressed as a string.
UserHostName
Returns the DNS name of the remote client.
UserLanguages
Returns a string array of the languages preferred by the browser/user.


The HttpRequest object is so frequently used that some application components, including Razor views, provide convenience properties so that you don't have to obtain an HttpContext object just to get an instance ofHttpRequest. Table 3-13 summarizes the convenience properties that are available for HttpRequest objects.


Table 3-13: Obtaining an HttpRequest Object in Different ASP.NET/MVC Components 
 Open table as spreadsheet
Component
Technique
Controller
Use the Request convenience property.
View
Use the Request convenience property.
Global Application Class
Use the Request convenience property.
Module
No convenience property is available. Use the HttpContext.Request property. (Modules are described in Chapter 4.)
Handler
No convenience property is available. Use the HttpContext.Request property. (Handlers are described in Chapter 5.)
Universally
You can always get the HttpRequest object for the current request through the static HttpContext.Current.Request property.

Figure 3-8 shows the request property values displayed by the view, for both an initial GET request and the POST request that results when the user selects a color and clicks the Vote button.

Image from book 
Figure 3-8: Displaying details of the request
In addition to the properties shown in Table 3-12, there are some properties that provide access to the data included in a request. I have shown these in Table 3-14, but they are not usually used directly in MVC controllers because model binding, which I describe in Pro ASP.NET MVC 5, is easier to work with. These properties are sometimes used in modules, however.


Table 3-14: Additional Properties Defined by the HttpRequest Class 
 Open table as spreadsheet
Name
Description
Files
Returns a collection of files sent by the browser in a form.
Form
Provides access to the raw form data.
Params
A collection of the combined data items from the query string, form fields, and cookies. You can also use an array-style indexer directly on the HttpRequest object, such that Request["myname"] is the same as Request.Params["myname"].
QueryString
Returns a collection of the query string parameters; this property isn't usually used directly in MVC applications.

Working with HttpResponse Objects

The HttpResponse object is the counterpart to HttpRequest and represents the response as it is being constructed. It also provides methods and properties that let you customize the response, something that is rarely required when using MVC views but that can be useful when working with other components, such as modules and handlers (described in Chapters 4 and 5).
Like HttpRequest, this class is essential to the way that ASP.NET processes requests and is used extensively within the MVC framework to generate the HTML (or other data) that is returned to clients. I have described the most important methods and properties in Table 3-15.



Table 3-15: The Most Useful Members of the HttpResponse Class 
 Open table as spreadsheet
Name
Description
AppendHeader(name, val)
Convenience method to add a new header to the response.
BufferOutput
Gets or sets a value indicating whether the request should be buffered completely before it is sent to the browser. The default value is true. Changing this to false will prevent subsequent modules and handlers from being able to alter the response.
Cache
Returns an HttpCachePolicy object that specifies the caching policy for the response. I describe the ASP.NET caching services in Chapters 11 and 12.
CacheControl
Gets or set the cache-control HTTP header for the response.
Charset
Gets or sets the character set specified for the response.
Clear()
The Clear and ClearContent methods are equivalent, and they remove any content from the response.
ClearContent()
ClearHeaders()
Removes all of the headers from the response.
ContentEncoding
Gets or sets the encoding used for content in the response.
Headers
Returns the collection of headers for the response.
IsClientConnected
Returns true if the client is still connected to the server.
IsRequestBeingDirected
Returns true if the browser will be sent a redirection.
Output
Returns a TextWriter that can be used to write text to the response.
OutputStream
Returns a Stream that can be used to write binary data to the response.
RedirectLocation
Gets or sets the value of the HTTP Location header.
Status
Gets or sets the status for the response; the default is 200 (OK).
StatusCode
Gets or sets the numeric part of the status; the default is 200.
StatusDescription
Gets or sets the text part of the status; the default is (OK).
SuppressContent
When set to true, this property prevents the response content from being sent to the client.
Write(data)
Writes data to the response output stream.
WriteFile(path)
Writes the contents of the specified file to the output stream.

In Table 3-16, I have summarized the convenience properties for a range of ASP.NET and MVC framework components.



Table 3-16: Obtaining an HttpResponse Object in Different ASP.NET/MVC Components 
 Open table as spreadsheet
Component
Technique
Controller
Use the Response convenience property.
View
Use the Response convenience property.
Global Application Class
Use the Response convenience property.
Module
No convenience property is available. Use the HttpContext.Response property. (Modules are described in Chapter 4.)
Handler
No convenience property is available. Use the HttpContext.Response property. (Handlers are described in Chapter 5.)
Universally
You can always get the current HttpResponse object through the static HttpContext.Current.Response property.