Implementation Patterns for HTTP Handlers
Extend ASP.NET Apps with Custom HTTP Handlers
October 30, 2009
ASP.NET Under the Hood
LANGUAGES:VB.NET | C#
ASP.NETVERSIONS: 2.0
Implementation Patterns for HTTP Handlers
Extend ASP.NET Apps with Custom HTTP Handlers
By Michele Leroux Bustamante
Greetings ASP.NET architects and developers! Join me inanother fun-filled installment of ASP.NET Under the Hood as we unravel some ASP.NETmysteries!
Q. I read your April column that discussed implementing acustom HTTP module for guest authentication; could you talk about HTTP handlersnext, and possibly explain the different reasons for implementing a customhandler?
A. ASP.NET extensibility is definitely one of my favoritetopics. Although it has been covered before, I ll take this opportunity toreview how HTTP handlers are engaged in processing requests, and summarize someof the implementation patterns that best fit the creation of custom handlers.
How Does ASP.NET Process Requests?
The first step in understanding HTTP handlers is tounderstand how the ASP.NET runtime processes all requests through a handler!IIS engages the ASP.NET runtime if the request is for an extension that ismapped to ASP.NET. Figure 1 illustrates this configuration for a Web site orvirtual directory in IIS.
Figure 1: IIS maps file extensionsto ISAPI extensions like the aspnet_isapi.dll.
In the case of IIS 5.0, the IIS runtime instance(inetinfo.exe) is responsible for sending the request to ASP.NET over a namedpipe (IPC) channel. In the case of IIS 6.0, the http.sys kernel receives allincoming requests to IIS and uses inetinfo.exe only as a metabase for mappingthe request to the correct application pool. This improves reliability andperformance ... but I digress ... we re talking about what happens after ASP.NET gets the request! Ineither case (IIS 5.0 or 6.0), after ASP.NET receives the request stream, itpasses it to a particular HTTP handler based on the application configuration.
For each request the configurationsection drives which component will handle incoming requests. For example, all.aspx pages are handled by PageHandlerFactory; all .asmx pages are handled byWebServiceHandlerFactory; and all .cs, .vb, and .config files are handled byHttpForbiddenHandler, as shown here:
"System.Web.UI.PageHandlerFactory"/>
"System.Web.Services.Protocols.WebServiceHandlerFactory,
System.Web.Services, .../>
"System.Web.HttpForbiddenHandler"/>
"System.Web.HttpForbiddenHandler"/>
"System.Web.HttpForbiddenHandler"/>
The default configuration (machine.config) providessettings for all applications, however it can. Any component that implementsthe IHttpHandlerFactory or IHttpHandler interface can be configured in thissection. Ultimately, the request is handled by an HTTP handler, and the factory sjob is to return the appropriate handler for the type of request. For example,the PageHandlerFactory is responsible for creating a particular Page objectbased on the .aspx page requested. Each Page object behind a Web formimplements IHttpHandler!
To put the job of the HTTP handler factory and HTTPhandler into another perspective, Figure 2 illustrates the sequence of applicationevents around the execution of a handler. As soon as the runtime determinesthat the requested page output has not been cached, the handler is created. Ifa factory is configured for the request type, the factory is delegated thisresponsibility and must return a valid IHttpHandler object from the GetHandleroperation. Every handler implements a ProcessRequest method, and this is whereall the goodness happens to provide a meaningful response. After processing therequest, the request lifecycle winds down with some post-processing events thatmay end up in caching the page output or some other detail before the requestcompletes.
Figure 2: The execution of an HTTPhandler in the context of a round-trip.
In short, the HTTP handler does all the work, depending onthe request. When you create a custom handler you are looking to provide someform of custom processing beyond what we get for free with ASP.NET pages, Webservices, and other configured resources. In the rest of this article I llillustrate creating a custom HTTP handler, or handler factory as appropriate,and discuss some useful scenarios for their implementation.
Quick Review of IHttpHandlerFactory and IHttpHandler
HTTP handler factories have one core function: toinstantiate an HTTP handler according to the request. They implement theinterface IHttpHandlerFactory from the System.Web namespace:
public interface IHttpHandlerFactory
{
IHttpHandlerGetHandler(HttpContext context,
string requestType,string url, string pathTranslated);
voidReleaseHandler(IHttpHandler handler);
}
ASP.NET passes enough information to the GetHandleroperation so that the factory can determine which HTTP handler to instantiateand return to the runtime for later use.
HTTP handlers implement the interface IHttpHandler, alsofrom the System.Web namespace:
public interface IHttpHandler
{
voidProcessRequest(HttpContext context);
bool IsReusable { get; }
}
As mentioned earlier, ProcessRequest is called by theASP.NET runtime, so this is where the handler does its work. Ultimately, itsjob is to process the incoming request stream and return a meaningful responseto the response stream before returning to the runtime to complete the requestcycle. To configure a custom handler factory or handler, you add entries to the section mentioned earlier, which I ll illustrate as we diginto the samples.
Creating a Simple Handler
In its simplest form, a handler is an endpoint that allowsyou to process a request and send a response. The most common endpoints for a Webapplication are pages (.aspx) and Web services (.asmx), but they also containsignificant processing logic for their respective roles. Sometimes you justwant to expose an endpoint that will allow you to generate a custom responsewithout the overhead. This is where implementing a simple handler using the.ashx extension comes in handy.
You can add a new .ashx resource to your Web applicationusing the Generic Handler template, as shown in Figure 3.
Figure 3: Adding a generic handlerto a Web site.
This adds an endpoint with the extension .ashx to yourproject, with the <%@ WebHandler %> directive indicating it s a handler.The class associated with the endpoint implements IHttpHandler. That means theoutput for invoking this .ashx endpoint is, well, whatever you want to put intothe ProcessRequest functionality!
The main driver for using a Web handler instead of a Pageobject is to provide content that is not page-centric. For example, to formatimages presented to the browser in a picture frame, you might want to provide afriendly URL like this: http://localhost/HttpHandlers/ShowImage.ashx?myphoto.ProcessRequest for this endpoint can take the query string, find the associatedimage, and present it in a framed table layout. The code for thisimplementation is shown in Figure 4. (The Generic Handler template inserts aninline implementation of the IHttpHandler class, as shown in Figure 4. It ispreferable to put the class into a code-behind file, as you ll see in the C#version of the sample code.)
<%@ WebHandler Language="VB"Class="ShowImage" %>
Imports System
Imports System.Web
Public Class ShowImage : Implements IHttpHandler
Public SubProcessRequest(ByVal context As HttpContext)
ImplementsIHttpHandler.ProcessRequest
context.Response.ContentType = "text/plain"
context.Response.Write("")
Ifcontext.Request.QueryString.Count = 0 Then
Throw NewHttpException("Page not accessible,
must provide a querystring")
End If
Dim qs As String =context.Request.QueryString(0)
context.Response.Write("
'Images/framebig.JPG'width=540 height=568>
")
context.Response.Write("")
End Sub
Public ReadOnly PropertyIsReusable() As Boolean
ImplementsIHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
Figure 4: Take thequery string, find the associated image, and present it in a framed tablelayout.
Web handlers like this can also be leveraged behind thescenes to format sections of page output. For example, a page that displaysphotos normally provides an tag that references the relative serverpath to the actual image, as shown here:
If you wanted the image to be formatted with a watermark,you can wrap the functionality in a Web handler and reference it from the srcattribute, like this:
The handler should write the formatted content directly tothe output stream, as shown here:
Dim file As String = context.Request.MapPath(qs)
Dim bmp As Bitmap = GraphicUtility.GenerateWatermark(file,
"SAMPLE",WatermarkPosition.Middle)
context.Response.ContentType = "image/Jpeg"
bmp.Save(context.Response.OutputStream,
Imaging.ImageFormat.Jpeg)
So, simple handlers like .ashx are very useful forproviding new endpoints that supply content to the browser as a publicendpoint, or as a private endpoint that wraps how specific content is emitted.
Creating a Configurable Handler Component
Simple handlers have their place, but there are morepowerful ways to leverage handlers. You can also create handler components thatare configurable behind the scenes (no .ashx extension required) toautomatically handle specific extension mappings. For example, if you want allimages from the /ProtectedPhotos directory to be watermarked, you can configurea handler for that subdirectory to perform that function automatically. Thisrequires a few steps:
Configure IIS to pass requests for imageextensions to ASP.NET (.jpg, .gif, .bmp, etc.).
Create a WatermarkImageHandler type thatimplements IHttpHandler.
Configure WatermarkImageHandler to handlerequests for images in the /Photos directory.
If you open the IIS console from the Control Panel andexpand the Web Sites node, you should be able to find the virtual directory forthe sample application, named HttpHandlers. Right-click on this node and selectProperties; from the Directory tab select Configuration. From here you can adda new extension that maps toC:WINDOWSMicrosoft.NETFrameworkv2.0.50727aspnet_isapi.dll (check the pathon your machine) as shown in Figure 5.
Figure 5: Adding an extensionmapping for .jpg files.
As for creating the WatermarkImageHandler, you can add aclass to the project and implement IHttpHandler. Add the code to watermark animage in the ProcessRequest operation, as shown in Figure 6.
Imports System
Imports System.Web
Imports System.Drawing
imports System.Drawing.Imaging
Imports Microsoft.VisualBasic
Public Class WatermarkImageHandler
Implements IHttpHandler
Public SubProcessRequest(ByVal context As HttpContext)
Implements IHttpHandler.ProcessRequest
Dim response AsHttpResponse = context.Response
Dim request AsHttpRequest = context.Request
Try
Dim imageToRender AsString = request.FilePath
Dim effect AsGraphicEffect = GraphicEffect.Watermark
Dim myFormat AsImageFormat
IfimageToRender.IndexOf(".gif") <> -1 Then
myFormat =ImageFormat.Gif
ElseIfimageToRender.IndexOf(".jpg") <> -1 Then
myFormat =ImageFormat.Jpeg
Else
Throw New Exception("Invalidimage format!")
End If
Dim imageFile AsString =
context.Server.MapPath(imageToRender)
Dim objBitmap AsBitmap =
GraphicUtility.GenerateWatermark(imageFile,"SAMPLE",
WatermarkPosition.Middle)
objBitmap.Save(response.OutputStream,myFormat)
response.Flush()
Catch ex As Exception
context.Trace.Write(ex.Message)
End Try
End Sub
Public ReadOnly PropertyIsReusable() As Boolean
Implements IHttpHandler.IsReusable
Get
Return True
End Get
End Property
End Class
Figure 6: Add codein the ProcessRequest operation to watermark an image.
This handler assumes that the request is being processedfor images with the extension .gif or .jpg, and handles reading the image fromdisk, watermarking the image, then writing the image to the response stream. Forthis to work, ASP.NET needs to load the handler when .gif or .jpg files arerequested. The following code configures WatermarkImageHandler only for the/ProtectedPhotos subdirectory so that only those images are affected by therequest:
"WatermarkImageHandler"/>
"WatermarkImageHandler"/>
When Do I Need a Custom Handler Factory?
As mentioned earlier, you can tell ASP.NET to use ahandler or handler factory in the section. Using a customhandler factory is an obvious choice when there are one or more handlers thatmay be able to service the request. For example, I may want to watermark imagesfor unauthenticated users but allow authenticated users to see the originalimage. I can do this using an ImageHandlerFactory.
The code in Figure 7 illustrates an IHttpHandlerimplementation that uses the HttpContext to determine if the user has beenauthenticated. If not, the WatermarkImageHandler is created. If so, theStaticImageHandler is created.
Imports System
Imports System.Web
Public Class ImageHandlerFactory
ImplementsIHttpHandlerFactory
Public FunctionGetHandler(ByVal context As HttpContext,
ByVal requestType As String, ByVal url AsString, ByVal
pathTranslated AsString) As IHttpHandler Implements
IHttpHandlerFactory.GetHandler
Dim handler As Object =Nothing
Ifcontext.User.Identity.IsAuthenticated Then
handler = NewStaticImageHandler()
Else
handler = NewWatermarkImageHandler()
End If
Return handler
End Function
Public SubReleaseHandler(ByVal handler As IHttpHandler)
ImplementsIHttpHandlerFactory.ReleaseHandler
Return
End Sub
End Class
Figure 7: AnIHttpHandler implementation that uses the HttpContext to determine if the userhas been authenticated.
Configuring the handler factory follows a similar patternto the handler:
"ImageHandlerFactory"/>
"ImageHandlerFactory"/>
Conclusion
From this article you can gather that there are many waysto incorporate the various implementations of HTTP handlers and HTTP handler factoriesin your Web applications. The simple Web handler is great for providing newendpoints externally or internally when you are not overriding IIS extensionmappings. More importantly, they require much less overhead than the Pagehandler and give you full control over the output. Custom IHttpHandlercomponents make it possible to use a handler behind the scenes without alteringfile extensions, in addition to making the handler completely configurable withthe flip of a setting. Custom IHttpHandlerFactory componentssupport similar configurability, while supporting the choice of handler basedon the use case. In all cases, you have full access to the HttpContext tointeract intelligently with the round trip for each request.
If you have questions or comments regarding this column, orany other ASP.NET topics, please drop me a line at [email protected] for reading!
C# and VB.NET codeexamples are available for download.
Michele LerouxBustamante is Chief Architect at IDesign Inc., Microsoft Regional Directorfor San Diego, Microsoft MVP for XML Web services, and a BEA TechnicalDirector. At IDesign Michele provides training, mentoring, and high-endarchitecture consulting services, specializing in scalable and secure .NETarchitecture design, globalization, Web services, and interoperability withJava platforms. She is a board member for the International Association ofSoftware Architects (IASA), a frequent conference presenter, conference chairof SD s Web Services track, and a frequently published author. She is currentlywriting a book for O Reilly on the Windows Communication Foundation. Reach herat http://www.idesign.net or http://www.dasblonde.net.
Additional Resources
IDesign: http://www.idesign.net
Michele s blog: http://www.dasblonde.net
Michele s WCF book: http://www.thatindigogirl.com
About the Author
You May Also Like