Gain Control of ASP.NET Requests
Unleash the Real Power of HTTP Handlers
October 30, 2009
CoreCoder
LANGUAGES: C#
ASP.NETVERSIONS: 2.0 | 3.5
Gain Control of ASP.NET Requests
Unleash the Real Power of HTTP Handlers
By Dino Esposito
Often overlooked, HTTP handlers and modules are truly the building blocks of the ASP.NET platform. Any request for any resource that is managed by ASP.NET is always ultimately resolved by an HTTP handler, and during its server-side lifetime passes through the pipeline of HTTP modules. At the end of the pipeline, the original HTTP packet has evolved into a piece of markup to bind to an HTTP response packet with a given set of headers, cookies, and content type settings.
An HTTP handler is the component that actually takes care of generating the response for any requests. An HTTP handler, though, it is just one cog in the ASP.NET runtime machine. It occupies a particular location in the overall lifecycle that each request undergoes when processed on the server. Such a lifecycle is defined through a sequence of global application events, from BeginRequest to EndRequest, that HTTP modules can hook up in order to filter and alter the ongoing packet. In this article, I ll focus on ASP.NET HTTP handlers and illustrate some scenarios where the use of custom HTTP handlers may lead your sites to a better performance and more flexible and powerful solutions.
HTTP Handlers In Person
An HTTP handler is an instance of a class that implements the IHttpHandler interface. The interface is a fairly simple one and consists of two members-the ProcessRequest method and the IsReusable property, as outlined below:
public interface IHttpHandler
{
// Methods
void ProcessRequest(HttpContext context);
// Properties
bool IsReusable { get; }
}
Overall, the IHttpHandler defines the contract that ASP.NET requires from components aimed at synchronously processing HTTP requests. ASP.NET can serve a Web request synchronously or asynchronously. How is either option chosen? Let s briefly summarize what happens under the hood before a request makes its official debut in the ASP.NET, runtime causing the runtime to fire the BeginRequest global event.
Each request handed over to ASP.NET is handled by a worker process that hosts the CLR. Each ASP.NET application currently running on a Web server is reserved one AppDomain inside the CLR instance. In the AppDomain, the first request for the application triggers the instantiation of a singleton instance of the HttpRuntime class the central console of the ASP.NET runtime engine.
The static constructor of the HttpRuntime class initializes a bunch of static data members as soon as the class is referenced for the first time. The cache for the application, instead, was created as part of the process that facilitated the creation of a new AppDomain for the application.
When the first request hits the application, a static method on the HttpRuntime class is invoked. Its method is ProcessRequest, and all it does is invoke a processing method on the singleton instance after creating the HTTP context for the particular request.
Next, the processing method that controls the work being done around the request figures out the HTTP handler object in charge of the request. The handler object is first checked to see if it implements the IHttpAsyncHandler interface an asynchronous handler. Otherwise, it is assumed that it represents a synchronous object. The configuration for the resource in the web.config points the ASP.NET runtime to loading a class that must implement IHttpHandler, IHttpAsyncHandler, or both. In any case, the async interface is given priority.
Once an instance of the configured HTTP handler has been loaded in memory, its ProcessRequest method is invoked and anything this method writes out to the response stream is actually poured into the pipeline. At the end, any content is sent to the browser as the modules in the pipeline have further modified it.
The ProcessRequest Method
Listing 1 shows a very simple, yet effective, HTTP handler. The logic behind the handler is largely concentrated in the ProcessRequest method.
using System.Web;
namespace Samples
{
public class SimpleHttpHandler : IHttpHandler
{
// Override the ProcessRequest method
public void ProcessRequest(HttpContext context)
{
context.Response.Write("
I'm an HTTP handler
");
}
// Override the IsReusable property
public bool IsReusable
{
get { return true; }
}
}
}
Listing 1: A sample HTTP handler.
The only purpose of this method is to produce some results for the caller. The input is the HTTP context of the current request through which the developer can reach session state, cache, request, and response objects, as well as user and trace information.
The results sent to the caller in most cases are HTML markup; but it can be any other content type. The Response object, in fact, offers a ContentType property to use accordingly. The following code snippet shows how to serve an image to the caller:
Bitmap bmp = CreateOrLoadImage();
:
context.Response.ContentType = "image/jpeg";
bmp.Save(context.Response.OutputStream, ImageFormat.Jpeg);
bmp.Dispose();
The System.Web.UI.Page class is the most popular HTTP handler in ASP.NET. The Page class implements the IHttpHandler interface; this implementation is automatically inherited by any of your code-behind classes. The ProcessRequest method on the Page class does a number of important things. In a nutshell, it is responsible for the server-side page lifecycle and includes operations such as loading the viewstate, restoring the last good known state for controls, handling the postback, firing events such as Page_Load, rendering, viewstate serialization, and page unloading.
All these tasks are simply a big overhead if you re not going to process a page request. For example, if you want to serve a dynamically generated image to an tag, make sure you don t use an extension that is mapped to the System.Web.UI.Page class. It will work, but it won t be as effective as it should be. I ll return to this point very soon, with an example.
The IsReusable Property
The IsReusable property is one of least understood, yet inherently simple, properties of the whole ASP.NET framework. A Boolean, read-only property, it indicates whether another request can reuse the same instance of the HTTP handler. The question might be rephrased as, would you like the handler to be a singleton that receives any calls or a new instance should be created for each request?
The IsReusable property on the Page class returns false, meaning that the page handler is never cached and reused. Most of the time, it doesn t really matter what you return, be it true or false. If you set the property to return false, then you require that a new object is allocated for each request. The simple allocation of an object is not a particularly expensive operation. However, the initialization of the handler might be costly. In this case, making the handler reusable saves much of the overhead. If the handler doesn t hold any state, there s no reason for not making it reusable.
In summary, I d say that IsReusable should be always set to true, except when you have instance properties to deal with or properties that may cause trouble if used in a concurrent environment. If you have no initialization tasks, it doesn t really matter whether it returns true or false.
Registering a Handler
An HTTP handler is bound to a path, with optional wildcard characters. Whenever ASP.NET detects a request that matches the path, the handler class is instantiated or retrieved from a local pool (see IsReusable) and set to work. Here s some sample code you need to register a handler. It comes from an application s web.config file:
path="*.xyz"
type="Samples.YourHandler" />
validate="false"
Each request that matches path and HTTP verb are resolved through the specified class. The validate attribute indicates whether ASP.NET should check the handler type for existence upon application start-up (if true) or on demand (if false).
Let s briefly reconsider the previous code snippet. Normally, ASP.NET applications do not serve XYZ resources because they haven t been instructed to do so. Will the be enough to force a given ASP.NET application to serve XYZ resources via the YourHandler class? The answer is no. Let s see why.
Web requests reach IIS first and are forwarded to ASP.NET only if the IIS metabase recognizes the extension of the requested URL and knows that it is bound to ASP.NET. So, while you can certainly use custom handlers to serve virtually any requests for any resources, how you get that depends on the particular extension requested.
Along with well-known extensions such as ASMX, ASPX, and ASCX, IIS also maps to ASP.NET a couple of extensions specifically created for custom HTTP handlers (AXD and ASHX). As a result, to enable any HTTP handler bound to an AXD or ASHX, resource you only need to configure the ASP.NET application through web.config and no work on the IIS metabase is required. It should be noted that this behavior applies to any version of IIS, including version 7.
Registering Handlers in IIS 7
In IIS 7, extensibility through handlers has been largely improved. You can now register a custom request handler to operate at the server level. You need the following configuration script in the web.config of IIS 7:
path="*.xyz"
type="Samples.YourHandler, handlerAssembly" />
Alternatively, you can use the IIS Management Console or the appcmd tool to edit the configuration. In this case, the code would run directly inside IIS without involving the ASP.NET runtime.
Serving Images via Handlers
It s clear that HTTP handlers provide the greatest flexibility as far as extensibility of ASP.NET requests is concerned. By writing an HTTP handler for a group of extensions (say, *.xyz), or just a particular resource (say, folder.axd) gives you the power of deciding how the system should react, step by step. Incidentally, note that the resource you bind to an HTTP handler doesn t have to be necessarily an existing server file. Sure, it could reference to a disk file, but the format of the file and its location are not bound in any way through restrictive rules. In addition, just because your handler is totally responsible for the output, the resource can also be a fixed name with no file and content behind.
What would be a great scenario for using HTTP handlers? Many Web sites serve dynamically generated images. How would you reference such an image in a Web page? Here s a common example:
The browser invokes the URL for dynimage.aspx each time it is called to display the image. The dynimage.aspx page can be as simple as that shown in Listing 2.
void Page_Load(object sender, EventArgs e)
{
Bitmap bmp = CreateOrLoadImage(Request);
:
Response.ContentType = "image/jpeg";
bmp.Save(Response.OutputStream, ImageFormat.Jpeg);
bmp.Dispose();
}
Listing 2: Serving images via ASPX pages.
A similar solution will certainly work just fine, but it s not the best you can ever do. An ASPX resource like dynimage.aspx is mapped to the code-behind class you have written as dynimage.aspx.cs, or the Page class if you don t provide a code-behind. Therefore, the whole standard lifecycle for the page will be run even though no viewstate is involved, because there s no form submission. Anyway, the request still passes through a series of events that can be made shorter to shave off some CPU cycles per request.
An idea is using an HTTP handler that will bring you a double benefit: faster processing and the opportunity of running a custom logic to retrieve and serve the image itself. Create an ASHX handler as shown here:
<%@ WebHandler Language="C#" Class="Samples.DbImageHandler" %>
Listing 3 shows some details of the DbImageHandler class. This class, in particular, delineates a handler that accepts an ID parameter on the query string and uses that to arrange a query to Northwind.
public class DbImageHandler : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
// Ensure the URL contains an ID argument being a number
int id = -1;
bool result = Int32.TryParse(context.Request.QueryString["id"], out id);
if (!result)
context.Response.End();
string cns = ConfigurationManager.ConnectionStrings["..."].ConnectionString;
string cmdText = "SELECT photo FROM employees WHERE employeeid=@id";
// Get an array of bytes from the BLOB field
byte[] img = null;
SqlConnection conn = new SqlConnection(cns);
using (conn)
{
SqlCommand cmd = new SqlCommand(cmdText, conn);
cmd.Parameters.AddWithValue("@id", id);
conn.Open();
img = (byte[])cmd.ExecuteScalar();
conn.Close();
}
// Prepare the response for the browser
if (img != null)
{
context.Response.ContentType = "image/jpeg";
// The 78-byte offset is required because of the particular
//(and non-standard) structure of the pictures stored in the
// Employees table of the Northwind database
context.Response.OutputStream.Write(img, 78, img.Length);
}
}
public bool IsReusable
{
get { return true; }
}
}
Listing 3: Serving images via ASPX pages.
The bytes retrieved from a blob field are served as a JPEG stream to the client. Here s how you use the handler from an ASP.NET page:
string url = String.Format("dbimage.ashx?id={0}", EmpList.SelectedValue);
Image1.ImageUrl = url;
Conclusion
HTTP handlers and HTTP modules are the building blocks of the ASP.NET platform, and have been since the beginning. ASP.NET includes several predefined handlers and HTTP modules, but developers can write handlers and modules of their own to perform a variety of tasks. HTTP handlers, in particular, are faster than ordinary Web pages and can be used in all circumstances in which you don t need state maintenance and postback events. To generate images dynamically on the server, for example, an HTTP handler is more efficient than a page.
The sample code accompanyingthis article is available for download.
Dino Esposito specializes mainly in ASP.NET, AJAX and RIA solutions and is the author of the upcomingProgramming ASP.NET 3.5 Core Reference (Microsoft Press, 2008). He also wrote Introducing ASP.NET AJAX, always for Microsoft Press. Late breaking news are available athttp://weblogs.asp.net/despos.
About the Author
You May Also Like