ASP.NET Web Forms Extensibility: Handlers
In the .NET world, all HTTP requests, whether they be for web services (XML, WCF, Web API), pages (Web Forms and MVC), etc, are processed by a handler. Basically, a handler is a particular implementation of the IHttpHandler interface, and requests are routed to a particular handler class by one of four ways:
An entry on the Web.config
file, on the httpHandlers
A route handler, like in MVC or Dynamic Data;
Explicitly requested by the URL, in the case of ASHX generic handlers.
The httpHandlers section can specify both a handler or a handler factory for a specific URL pattern (say, for example, /images/*.png), which may be slightly confusing. I have already discussed handler factories in another post, have a look at it if you haven’t already. A simple registration would be:
2: <add verb="*" path="Image.axd" type="MyNamespace.MyHandler, MyAssembly"/>
Another option is through a route. The IRouteHandler interface defines a method GetHttpHandler which returns the route handler that will handle the request. You can register a IRouteHandler instance for a specific route by setting the RouteHandler property inside the Route class.
Finally, there’s another kind of handler that doesn’t need registering and that is called explicitly: generic handlers. These are .ASHX markup files without any user interface elements that merely reference a code-behind class, which must implement IHttpHandler (you can also place code in the .ASHX file, inside a <script runat=”server”> declaration). Here’s an example:
1: <%@ WebHandler Language="C#" Class="Handler" %>
2: <script runat="server" language="C#">
2: public class Handler : System.Web.IHttpHandler
Having said that, what is a handler good for? The IHttpHandler interface only defines one method, ProcessRequest, and a property, IsReusable. As you can tell, this is considerably more simple than, for example, the Page class, with its myriad of virtual methods and events, which, of course, is also an implementation of IHttpHandler. Because of that, it is much more useful for handling requests that do not need a complex lifecycle. Some scenarios:
Downloading a file;
Uploading a file;
Generating content dynamically, such as images.
The IsReusable indicates to the ASP.NET infrastructure if the hander’s instance can be reused for different identical requests or if a new instance needs to be created. If you don’t store state on the handler’s class, it is safe to return true.
As for the ProcessRequest method, a simple implementation might be:
1: public class ImageHandler : IHttpHandler
3: public void ProcessRequest(HttpContext context)
5: Bitmap bmp = new Bitmap(400, 300);
7: Graphics g = Graphics.FromImage(bmp);
8: g.DrawRectangle(new Pen(new SolidBrush(Color.Green)), 10, 10, 300, 200);
9: g.DrawString(context.Request.Url.Query, new Font("Arial", 30), new SolidBrush(Color.Yellow), new PointF(10f, 10f));
11: context.Response.ContentType = "image/gif";
13: bmp.Save(context.Response.OutputStream, ImageFormat.Gif);
16: public Boolean IsReusable
20: return (true);
This will create an image with a text string that is obtained from the query string, that is, anything in the URL after the ? symbol.
Don’t forget to always return the appropriate content type, because the browser won’t know how to handle the content you send without it.
One final note: from an handler you normally don't have access to the session - the Session property is null. If you need to use it, you must declare that your handler implements IRequiresSessionState or IReadOnlySessionState, the later for read-only access. That's basically what ASP.NET does when, on your page's markup, you place a EnableSessionState attribute.