Implementation Patterns for HTTP Handlers

Extend ASP.NET Apps with Custom HTTP Handlers

ASP.NET Under the Hood

LANGUAGES: VB.NET | C#

ASP.NET VERSIONS: 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 in another fun-filled installment of ASP.NET Under the Hood as we unravel some ASP.NET mysteries!

 

Q. I read your April column that discussed implementing a custom HTTP module for guest authentication; could you talk about HTTP handlers next, and possibly explain the different reasons for implementing a custom handler?

 

A. ASP.NET extensibility is definitely one of my favorite topics. Although it has been covered before, I ll take this opportunity to review how HTTP handlers are engaged in processing requests, and summarize some of 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 to understand 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 is mapped to ASP.NET. Figure 1 illustrates this configuration for a Web site or virtual directory in IIS.

 


Figure 1: IIS maps file extensions to 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 named pipe (IPC) channel. In the case of IIS 6.0, the http.sys kernel receives all incoming requests to IIS and uses inetinfo.exe only as a metabase for mapping the request to the correct application pool. This improves reliability and performance ... but I digress ... we re talking about what happens after ASP.NET gets the request! In either case (IIS 5.0 or 6.0), after ASP.NET receives the request stream, it passes it to a particular HTTP handler based on the application configuration.

 

For each request the configuration section drives which component will handle incoming requests. For example, all .aspx pages are handled by PageHandlerFactory; all .asmx pages are handled by WebServiceHandlerFactory; and all .cs, .vb, and .config files are handled by HttpForbiddenHandler, 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) provides settings for all applications, however it can. Any component that implements the IHttpHandlerFactory or IHttpHandler interface can be configured in this section. Ultimately, the request is handled by an HTTP handler, and the factory s job is to return the appropriate handler for the type of request. For example, the PageHandlerFactory is responsible for creating a particular Page object based on the .aspx page requested. Each Page object behind a Web form implements IHttpHandler!

 

To put the job of the HTTP handler factory and HTTP handler into another perspective, Figure 2 illustrates the sequence of application events around the execution of a handler. As soon as the runtime determines that the requested page output has not been cached, the handler is created. If a factory is configured for the request type, the factory is delegated this responsibility and must return a valid IHttpHandler object from the GetHandler operation. Every handler implements a ProcessRequest method, and this is where all the goodness happens to provide a meaningful response. After processing the request, the request lifecycle winds down with some post-processing events that may end up in caching the page output or some other detail before the request completes.

 


Figure 2: The execution of an HTTP handler in the context of a round-trip.

 

In short, the HTTP handler does all the work, depending on the request. When you create a custom handler you are looking to provide some form of custom processing beyond what we get for free with ASP.NET pages, Web services, and other configured resources. In the rest of this article I ll illustrate 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: to instantiate an HTTP handler according to the request. They implement the interface IHttpHandlerFactory from the System.Web namespace:

 

public interface IHttpHandlerFactory

{

 IHttpHandler GetHandler(HttpContext context,

  string requestType, string url, string pathTranslated);

 void ReleaseHandler(IHttpHandler handler);

}

 

ASP.NET passes enough information to the GetHandler operation so that the factory can determine which HTTP handler to instantiate and return to the runtime for later use.

 

HTTP handlers implement the interface IHttpHandler, also from the System.Web namespace:

 

public interface IHttpHandler

{

 void ProcessRequest(HttpContext context);

 bool IsReusable { get; }

}

 

As mentioned earlier, ProcessRequest is called by the ASP.NET runtime, so this is where the handler does its work. Ultimately, its job is to process the incoming request stream and return a meaningful response to the response stream before returning to the runtime to complete the request cycle. To configure a custom handler factory or handler, you add entries to the section mentioned earlier, which I ll illustrate as we dig into the samples.

 

Creating a Simple Handler

In its simplest form, a handler is an endpoint that allows you to process a request and send a response. The most common endpoints for a Web application are pages (.aspx) and Web services (.asmx), but they also contain significant processing logic for their respective roles. Sometimes you just want to expose an endpoint that will allow you to generate a custom response without 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 application using the Generic Handler template, as shown in Figure 3.

 


Figure 3: Adding a generic handler to a Web site.

 

This adds an endpoint with the extension .ashx to your project, with the <%@ WebHandler %> directive indicating it s a handler. The class associated with the endpoint implements IHttpHandler. That means the output for invoking this .ashx endpoint is, well, whatever you want to put into the ProcessRequest functionality!

 

The main driver for using a Web handler instead of a Page object is to provide content that is not page-centric. For example, to format images presented to the browser in a picture frame, you might want to provide a friendly URL like this: http://localhost/HttpHandlers/ShowImage.ashx?myphoto. ProcessRequest for this endpoint can take the query string, find the associated image, and present it in a framed table layout. The code for this implementation is shown in Figure 4. (The Generic Handler template inserts an inline implementation of the IHttpHandler class, as shown in Figure 4. It is preferable 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 Sub ProcessRequest(ByVal context As HttpContext)

  Implements IHttpHandler.ProcessRequest

   context.Response.ContentType = "text/plain"

   context.Response.Write("")

   If context.Request.QueryString.Count = 0 Then

     Throw New HttpException("Page not accessible,

      must provide a query string")

   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 Property IsReusable() As Boolean

  Implements IHttpHandler.IsReusable

   Get

     Return False

   End Get

 End Property

End Class

Figure 4: Take the query string, find the associated image, and present it in a framed table layout.

 

Web handlers like this can also be leveraged behind the scenes to format sections of page output. For example, a page that displays photos normally provides an tag that references the relative server path 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 src attribute, like this:

 

 

The handler should write the formatted content directly to the 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 for providing new endpoints that supply content to the browser as a public endpoint, 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 more powerful ways to leverage handlers. You can also create handler components that are configurable behind the scenes (no .ashx extension required) to automatically handle specific extension mappings. For example, if you want all images from the /ProtectedPhotos directory to be watermarked, you can configure a handler for that subdirectory to perform that function automatically. This requires a few steps:

  • Configure IIS to pass requests for image extensions to ASP.NET (.jpg, .gif, .bmp, etc.).
  • Create a WatermarkImageHandler type that implements IHttpHandler.
  • Configure WatermarkImageHandler to handle requests for images in the /Photos directory.

 

If you open the IIS console from the Control Panel and expand the Web Sites node, you should be able to find the virtual directory for the sample application, named HttpHandlers. Right-click on this node and select Properties; from the Directory tab select Configuration. From here you can add a new extension that maps to C:\WINDOWS\Microsoft.NET\Framework\v2.0.50727\aspnet_isapi.dll (check the path on your machine) as shown in Figure 5.

 


Figure 5: Adding an extension mapping for .jpg files.

 

As for creating the WatermarkImageHandler, you can add a class to the project and implement IHttpHandler. Add the code to watermark an image 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 Sub ProcessRequest(ByVal context As HttpContext)

   Implements IHttpHandler.ProcessRequest

   Dim response As HttpResponse = context.Response

   Dim request As HttpRequest = context.Request

   Try

     Dim imageToRender As String = request.FilePath

     Dim effect As GraphicEffect = GraphicEffect.Watermark

     Dim myFormat As ImageFormat

     If imageToRender.IndexOf(".gif") <> -1 Then

       myFormat = ImageFormat.Gif

     ElseIf imageToRender.IndexOf(".jpg") <> -1 Then

       myFormat = ImageFormat.Jpeg

     Else

       Throw New Exception("Invalid image format!")

     End If

     Dim imageFile As String =

       context.Server.MapPath(imageToRender)

     Dim objBitmap As Bitmap =

       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 Property IsReusable() As Boolean

   Implements IHttpHandler.IsReusable

   Get

     Return True

   End Get

 End Property

End Class

Figure 6: Add code in the ProcessRequest operation to watermark an image.

 

This handler assumes that the request is being processed for images with the extension .gif or .jpg, and handles reading the image from disk, watermarking the image, then writing the image to the response stream. For this to work, ASP.NET needs to load the handler when .gif or .jpg files are requested. The following code configures WatermarkImageHandler only for the /ProtectedPhotos subdirectory so that only those images are affected by the request:

 

 

   

     

      "WatermarkImageHandler"/>

     

      "WatermarkImageHandler"/>

   

 

 

When Do I Need a Custom Handler Factory?

As mentioned earlier, you can tell ASP.NET to use a handler or handler factory in the section. Using a custom handler factory is an obvious choice when there are one or more handlers that may be able to service the request. For example, I may want to watermark images for unauthenticated users but allow authenticated users to see the original image. I can do this using an ImageHandlerFactory.

 

The code in Figure 7 illustrates an IHttpHandler implementation that uses the HttpContext to determine if the user has been authenticated. If not, the WatermarkImageHandler is created. If so, the StaticImageHandler is created.

 

Imports System

Imports System.Web

Public Class ImageHandlerFactory

 Implements IHttpHandlerFactory

 Public Function GetHandler(ByVal context As HttpContext,

   ByVal requestType As String, ByVal url As String, ByVal

  pathTranslated As String) As IHttpHandler Implements

  IHttpHandlerFactory.GetHandler

   Dim handler As Object = Nothing

   If context.User.Identity.IsAuthenticated Then

     handler = New StaticImageHandler()

   Else

     handler = New WatermarkImageHandler()

   End If

   Return handler

 End Function

 

 Public Sub ReleaseHandler(ByVal handler As IHttpHandler)

 Implements IHttpHandlerFactory.ReleaseHandler

   Return

 End Sub

End Class

Figure 7: An IHttpHandler implementation that uses the HttpContext to determine if the user has been authenticated.

 

Configuring the handler factory follows a similar pattern to the handler:

 

 

   

     

      "ImageHandlerFactory"/>

     

      "ImageHandlerFactory"/>

   

 

 

Conclusion

From this article you can gather that there are many ways to incorporate the various implementations of HTTP handlers and HTTP handler factories in your Web applications. The simple Web handler is great for providing new endpoints externally or internally when you are not overriding IIS extension mappings. More importantly, they require much less overhead than the Page handler and give you full control over the output. Custom IHttpHandler components make it possible to use a handler behind the scenes without altering file extensions, in addition to making the handler completely configurable with the flip of a setting. Custom IHttpHandlerFactory components support similar configurability, while supporting the choice of handler based on the use case. In all cases, you have full access to the HttpContext to interact intelligently with the round trip for each request.

 

If you have questions or comments regarding this column, or any other ASP.NET topics, please drop me a line at [email protected]. Thanks for reading!

 

C# and VB.NET code examples are available for download.

 

Michele Leroux Bustamante is Chief Architect at IDesign Inc., Microsoft Regional Director for San Diego, Microsoft MVP for XML Web services, and a BEA Technical Director. At IDesign Michele provides training, mentoring, and high-end architecture consulting services, specializing in scalable and secure .NET architecture design, globalization, Web services, and interoperability with Java platforms. She is a board member for the International Association of Software Architects (IASA), a frequent conference presenter, conference chair of SD s Web Services track, and a frequently published author. She is currently writing a book for O Reilly on the Windows Communication Foundation. Reach her at 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

 

 

 

Hide comments

Comments

  • Allowed HTML tags: <em> <strong> <blockquote> <br> <p>

Plain text

  • No HTML tags allowed.
  • Web page addresses and e-mail addresses turn into links automatically.
  • Lines and paragraphs break automatically.
Publish