HTTP handlers (classed that derive from
System.Web.IHttpHandler) are a wonderful way to add richness to ASP.NET applications. They can also be used to serve custom dynamic content (say RSS feeds) or images, in addition to regular ASPX pages.
This article will introduce four different ways you can use HTTP handlers. The end result is the same, but I hope you will also find that by exploring different ways of using them you will also gain a better understanding of how things work. I will also show a typical error when using HTTP handlers from assemblies that are stored in the Global Assembly Cache (GAC).
When to use HTTP handlers?
I commonly use HTTP handlers to serve pictures from Web applications. Why would I need that? Pictures are often manipulated before being sent to the client. Such manipulation can include adding a watermark, resizing or applying other image transformations and manipulations. By applying these dynamically, I do not need to modify the original images: the application dynamically applies these modifications, and sends the modified content to the client. A good example could be generating thumbnails for an image gallery. Why bother doing this beforehand, when your application can just as well do it dynamically.
Of course usually the result can be cached, to improve performance - all transformations in an image gallery for example can be cached, because they will be the same, regardless of visitor. But it is not only image manipulation that can be achieved. If you have your images stored in a database, those too can be published in a web application by using HTTP handlers.
And handlers are not restricted to images, any content can be served using them. In fact (in case you do not know) the way ASP.NET serves ASPX pages is by the way of HTTP handlers. The
Page class itself implements
IHttpHandler that gets loaded and called for all pages with the .ASPX extension.
The Greetings Picture
Before we begin with the actual handlers, I want to show a very simple image manipulation class. What this does is create a new 640x120 sized bitmap with some greeting text written into it. It clears the background with white and writes in black Arial letters onto the surface. This is a very simple example, but you could do any image manipulation here.
using System.Drawing;
using System.Drawing.Imaging;
namespace MyLibrary
{
public static class ImageBuilder
{
public static Image CreateGreeting(string name)
{
Bitmap bmp = new Bitmap(640, 120, PixelFormat.Format32bppRgb);
using (Graphics g = Graphics.FromImage(bmp))
{
g.FillRectangle(Brushes.White, new Rectangle(0, 0, 640, 120));
g.DrawString(string.Format("Hello {0}!", name), new Font( "Arial", 28 ), Brushes.Black, new Point(10, 10));
}
return bmp;
}
}
}
This piece of code will be used by all of the examples I will present.
To begin with, create a new ASP.NET Web Application (project), and add the above code to the project (ImageBuilder.cs). Also note that I have tested the examples with VS 2010 and .NET 4.0.
The traditional code behind .ASHX
The easiest and fastest way to create an HTTP handler is by selecting
Generic Handler to be added to the project. This will create an .ASHX file and an .ASHX.CS file. Whenever you request the .ASHX file, the code-behind file will be invoked. However, in this case there are no controls, there are no predetermined content - you are in charge what you return.
Normally you override the
ProcessRequest() method, and return any content from within there. Now that you have create your Web Application project (where you saved ImageBuilder, see above) add a new
Generic Handler to it. Call it Handler1. This will add
Handler1.ashx and
Handler1.ashx.cs. You do not need to modify the ASHX file for now (we will get to its contents later). Now add the following code to the code behind file:
using System.Drawing;
using System.Drawing.Imaging;
using System.Web;
namespace HttpHandlerTesting
{
public class Handler1 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/jpeg";
string name = "Anonymous";
if (context.Request.QueryString["Name"] != null )
{
name = context.Request.QueryString["Name"];
}
Image img = MyLibrary.ImageBuilder.CreateGreeting(name);
img.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
public bool IsReusable { get { return false; } }
}
}
AS you can see the class is derived from
IHttpHandler. I will ignore the
IsReusable property here, you can read more about that in MSDN.
This code will generate an image with a greeting to either "Anonymous" or to the name specified in the QueryString parameter
Name. It will then return this image in the response. Notice that the
ContentType of the response is set manually to the MIME type of JPG images. This is needed so the browser can interpret the response. After this is done, the image is written to the [n]OutputStream of the response, thus sending the JPG data back to the client.
Try invoking it by starting your application and navigating to
Handler1.ashx?Name=Lenard. This will return an image with the text
Hello Lenard!. (because the image contains white background and black text, it might not be obvious it is an image. Feel free to use a tool like
Fiddler to verify it is indeed a JPG file! Alternatively, you could try to draw colorful lines and shapes in the background, but I leave that exercise to you
)
As you can see we only needed to add some files to the project to have a very simple handler. But there are more ways to use handlers, so let us process to Step #2.
A single .ASHX file
So now that you saw the code-behind version, lets merge the two files into a single file. But before we get started, lets examine the ASHX file. Open the
Handler1.ashx file. Simply double clicking might not work, right click on it and select view markup in this case.
<%@ WebHandler Language="C#" CodeBehind="Handler1.ashx.cs" Class="HttpHandlerTesting.Handler1" %>
The ASHX file is very similar to that of an .ASPX or .ASCX file. But it does not declare a Page, it declares a WebHandler. The file is empty except for this one line, which declares the class to use and the code behind file where the class can be found.
Now to make a single file handler, add
Handler2.ashx to the project. Maybe the easiest way to achieve this is to select to add a
Text document to the project in Visual Studio, and then type Handler2.ashx as the name.
<%@ WebHandler Language="C#" Class="Handler2" %>
public class Handler2 : System.Web.IHttpHandler
{
public void ProcessRequest(System.Web.HttpContext context)
{
context.Response.ContentType = "image/jpeg";
string name = "Anonymous";
if (context.Request.QueryString["Name"] != null )
{
name = context.Request.QueryString["Name"];
}
System.Drawing.Image img = MyLibrary.ImageBuilder.CreateGreeting(name);
img.Save(context.Response.OutputStream, System.Drawing.Imaging.ImageFormat.Jpeg);
}
public bool IsReusable { get { return false; } }
}
It is really not that different from the original version. We simply removed the
CodeBehind attribute and included the class definition in the body of the ASHX file. Is this solution better than the first? I will let you decide that
Code in another assembly
A more complex system could include the handler logic (the code) in another assembly, separate from the actual Web pages. In this case you can still use the .ASHX file to register your handler. I find this easier to do than getting dirty and registering your handler in the Web.config file - rest assured the next section will show you how to to that.
To proceed, add a
Class Library to the Solution, we will call it
MyLibrary. You will have to move
ImageBuilder to this new library. Then, proceed to add a new C# class to this new library, which we will call
Handler3:
using System.Drawing.Imaging;
using System.Web;
namespace MyLibrary
{
public class Handler3 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/jpeg";
string name = "Anonymous";
if (context.Request.QueryString["Name"] != null)
{
name = context.Request.QueryString["Name"];
}
System.Drawing.Image img = ImageBuilder.CreateGreeting(name);
img.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
public bool IsReusable { get { return false; } }
}
}
As you can see this is pretty much the same code we have been using before. Now comes the modification to the .ASHX file. Add
Handler3.ashx to the web project:
<%@ WebHandler Language="C#" Class="MyLibrary.Handler3" %>
Here we specify the namespace and the class. From here there are two ways to proceed.
In case MyLibrary is just your regular .NET class library (no strong names, etc), you can just add a reference to it to your web project. This will include MyLibrary.dll in the bin\ folder of the web application. It will also take care of the rest, and the above handler delcaration will work.
However, if your library has a strong name and you store it in Global Assembly Cache, you will get an ASP.NET error saying it does not find the library. As it turns out, for automatic binding to work the class that you refer to in the
Class attribute must be found in an assembly that is in your bin\ folder.
This error happens even if you have created a reference to the library.
To solve this problem, we simply need to add a reference to the assembly into the .ASHX file:
<%@ WebHandler Language="C#" Class="MyLibrary.Handler3" %>
<%@ Assembly Name="MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e4433e4487bd68da" %>
Of course make sure you use the correct identifier for the particular assembly you are working with. This will solve the problem with the reference, and now our handler works even from the GAC.
You might wonder when can you run into this problem? In SharePoint if you want to use an ASHX file and the handler code in a DLL, you will probably end up having this problem.
Handlers in Web.config
Having an ASHX file will make it easy to use handlers, because you do not need to register them. You can however register your handlers in Web.config. This gives you finer control over when your handler is called, what extensions it will serve, etc.
To finnish this example let's create the fourth version of the handler. Let's add this to the external class library project - MyLibrary - as well. We will call this class
Handler4.
using System.Drawing.Imaging;
using System.Web;
namespace MyLibrary
{
public class Handler4 : IHttpHandler
{
public void ProcessRequest(HttpContext context)
{
context.Response.ContentType = "image/jpeg";
string name = "Anonymous";
name = System.IO.Path.GetFileNameWithoutExtension(context.Request.Path);
System.Drawing.Image img = ImageBuilder.CreateGreeting(name);
img.Save(context.Response.OutputStream, ImageFormat.Jpeg);
}
public bool IsReusable { get { return false; } }
}
}
In this handler I changed the name fetching code. Instead of the QueryString parameter, we use the name of the request. Without extensions. As you will soon see, when we are registering a handler in Web.config, we can use a "star" name, and are not locked into a single name.
Now comes the change to Web.config. You will have to register the handler in either of two places (or perhaps both).
If you use IIS 6 or the Development Web Server in VS2008/2010, you will need to add this to the system.web section:
<system.web>
<httpHandlers>
<add verb="GET" path="*.jpg" type="MyLibrary.Handler4, MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e4433e4487bd68da"/>
</httpHandlers>
</system.web>
If you use IIS 7, 7.5 , you will need to add this to system.webServer:
<system.webServer>
<handlers>
<add name="MyHandler" verb="GET" path="*.jpg" type="MyLibrary.Handler4, MyLibrary, Version=1.0.0.0, Culture=neutral, PublicKeyToken=e4433e4487bd68da"/>
</handlers>
</system.webServer>
In either case you tell ASP.NET to map all request to *.jpg (that is all jpg files) that are HTTP GET requests to our handler. If you now request Lenard.jpg from the server, you will actually get our dynamic greeting image, which reads
Hello Lenard!.
There is nothing preventing you from simply specifying "Handler4.ashx" as the path attribute, but let's admit that would not be as much fun
Conclusion
HTTP handlers can be a lot of fun, and they are very lightweight (compared to the regular ASP.NET Page pipeline). With them, you can completely customize the way your application functions, because as you saw you can even return dynamic content when a "regular" file, like a .jpg image is requested from the server. As you saw, there are many ways to use a handler, and you should pick the one that fits the situation at hand best.