Introduction
This article seeks to demonstrate a potential use of the ASP.NET Pipeline architecture. The Handler technique of dealing with requests on the server-side is adopted to place a watermark string on all images sent to the client. The original image is however not modified in the process. A copy of the image is created and modified and flushed into the output stream connected to the client browser. If your website offers a picture gallery, this method could be used to put a custom message (watermark) into every image that gets rendered on the client browser. More often than not, users download images and forget where they came from. The technique employed by the code in this article can serve to effectively advertise the source of the image. With a little imagination, the basic idea can be used to provide custom captions for images as well. One could come up with a myriad of uses for this technique.
Background
Old ASP was based on sending a response for every client request that makes its way through several ISAPI filters installed on the IIS. These ISAPI filters and extensions required to be coded in C++, and hence was not widely adopted to follow, although, they offered great benefits by adding more punch to the services offered by the web server. However, ASP.NET takes the focus away from ISAPI and introduces the concepts of handlers and modules to meet this end. Read on, people!
Requests are received by IIS and passed to the ASP.NET Worker Process (aspnet_wp.exe) by an ISAPI filter (provided by ASP.NET) called aspnet_isapi.dll. This filter re-routes the request to the worker process, thereby bypassing a lot of IIS features in favor of those offered by the CLR and ASP.NET. The worker process dispatches HTTP requests through a pipeline which contains several modules that can modify and filter the requests. From that point on, the request is wrapped up into an instance of HttpContext
and piped through a number of ASP.NET classes that implement the IHttpModule
interface. There are a number of system-level HTTP modules, providing services ranging from authentication to state management to output caching. The number of modules that get to intercept the request is based upon settings within the host machine's machine.config file and the application's web.config file. In classic ASP, this role of providing pre- and post-processing fell upon ISAPI filters.
The ASP.NET pipeline represents a series of extensible objects that work in a sequential-chain and act on an incoming request, one after the other. As the requests pass through the pipeline, they are modified and filtered until they are finally handed over to a handler object that emits a suitable response back to the client. Through the use of these modules and handlers, we can effectively extend the capabilities of our web server, just like what ISAPI extensions and filters are used to do for IIS.
Every incoming request will have a URI. ASP.NET allows us to map every single URI to a specific handler class that will send a suitable response. A URI that is not mapped to a specific handler class will be passed to ASP.NET's default handler. This default handler will target the URI as a file name, loading the file specified within the URI.
By default, requests for a �.aspx� page are handled by a compiled class that inherits from the Page
class (the request handler in this case), which implements the IHttpHandler
interface. If we want another handler to cater to a request, we need to map the request to the desired handler class in the web application's configuration files, and also instruct the ASP.NET ISAPI extension to look out for the particular request. Classes implementing IHttpHandler
can hook into the HTTP pipeline and service requests through the interface's ProcessRequest
method.
The ASP.NET ISAPI extension will only pick those URI requests, it has been mapped or configured to acquire. This configuration has to be done in the IIS web server configuration property sheet.
This article will focus only on one potential use of HTTP handlers.
IHttpHandler
To create a class that acts as a HTTP handler, we must implement the IHttpHandler
interface. The interface has two prominent members that we must implement in our class: -
Sub ProcessRequest
(context)- called by ASP.NET to perform processing on the request context.
Property readonly IsReusable as Boolean
- called by ASP.NET to determine if our handler class can be reused for multiple requests.
Session State In HTTP Handlers
An HTTP handler does not have access to session state variables and objects by default. To acquire this privilege, the handler class also needs to implement either one of the following interfaces depending on the extent of access required: -
IRequiresSessionState
-implement if both read and access to session state is required.
IReadOnlySessionState
-implement if only read access to session state is required.
Both the above interfaces do not have any member signatures to implement. They simply serve as markers or flags for the ASP.NET engine to determine the degree of access to session information that must be provided to the Handler object.
Registering a Handler With The Application Configuration Files
When the ASP.NET engine receives a request, it will decide on which handler to invoke, by screening for <HttpHandler>
elements in the web.config file. This is what the element entry should look like in a configuration file: -
<httpHandler>
<add verb="" path="" type="" validate=""/>
</httpHandler>
Attribute-Value Options
verb
=["comma-delimited list of HTTP verbs like GET, PUT, POST" | script-mapping characters or strings].
e.g. verb="*", verb="GET, PUT". The verb
attribute is used when you want to restrict requests via 'POST' or 'GET' or 'HEAD'. You'll just want to stick with a '*' - this will allow all of the above.
path
=["single URL path" | "wildcard strings"].
e.g. path="*.aspx", path="resource/"
type
="fully-qualified class name, assembly name"
e.g. type="Namespace.Class, AssemblyName"
validate
=[true | false]-- If false, then ASP.NET will not load the class at startup until a matching request is received.
Using HTTP Handlers To Add watermark to images
Here, we will attempt to insert a custom watermark into every image file that is requested. To follow this sample, I expect you to have a basic idea of how to use the System.Drawing
namespace and classes. For the uninitiated, you use either a brush or a pen to draw on a canvas (to put it bluntly!).
Using the code
ImageHandler.vb
This class is our HTTP Handler. You could compile this class as a separate DLL assembly or as a part of another assembly...it is your call. However, note the name of the class, the namespace it belongs to and the assembly it is packaged in, as these pieces of information prove vital at the handler registration stage. The ASP.NET worker process passes the HttpContext
object (that wraps the request) to the ProcessRequest
routine. We obtain the physical path of the requested image on the server, and proceed to apply a watermark to the image. The resultant image is then written to the output stream of the Response
object. Follow the comments in the ImageWatermark
class to comprehend its logic.
Imports System.Web
Imports System.Drawing
Public Class ImageHandler
Implements IHttpHandler
Public ReadOnly Property IsReusable() _
As Boolean Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
Public Sub ProcessRequest(ByVal context _
As HttpContext) Implements IHttpHandler.ProcessRequest
Dim output As ImageWatermark = _
New ImageWatermark(context.Request.PhysicalPath)
output.AddWaterMark("This is the custome string")
output.Image.Save(context.Response.OutputStream, _
Drawing.Imaging.ImageFormat.Jpeg)
End Sub
Private Class ImageWatermark
Private bmp As Bitmap
Public Sub New(ByVal physicalPathToImage As String)
bmp = New Bitmap(physicalPathToImage)
End Sub
Public Sub AddWaterMark(ByVal watermark As String)
Dim canvas As Graphics
Try
canvas = Graphics.FromImage(bmp)
Catch e As Exception
Dim bmpNew As Bitmap = New Bitmap(bmp.Width, bmp.Height)
canvas = Graphics.FromImage(bmpNew)
canvas.DrawImage(bmp, New Rectangle(0, 0, _
bmpNew.Width, bmpNew.Height), 0, 0, _
bmp.Width, bmp.Height, GraphicsUnit.Pixel)
bmp = bmpNew
End Try
canvas.DrawString(watermark, _
New Font("Verdana", 14, FontStyle.Bold), _
New SolidBrush(Color.Beige), 0, 0)
End Sub
Public ReadOnly Property Image() As Bitmap
Get
Return bmp
End Get
End Property
End Class
End Class
At this point, I should probably bring your attention to the exception handling block employed in the AddWaterMark
routine of the ImageWatermark
class. It was not part of my original idea, simply because I did not expect to encounter the following error message:
Error: An unhandled exception of type
'System.Exception' occurred in system.drawing.dll
Additional information: A Graphics object cannot be
created from an image that has an indexed pixel format...
As it turns out, a GIF image with an indexed pixel format does not allow its color palette to be modified. As a workaround, we draw the contents of the image into a new Bitmap
class and proceed with our operation on the new instance. This error really annoyed me until I found out! Thanks to the Internet!
Registering the ImageHandler class
This part is easy. Just add the following XML section to the <System.Web>
element of your web.config file. Note that I have registered the handler to only deal with requests for GIF and JPG files. The assembly name provided is Home
. You should replace it with the name of the assembly you compile the handler class into.
<httpHandlers>
<add verb="*" path="*.jpg,*.gif" type="ImageHandler,Home" validate="false"/>
</httpHandlers>
Configuring IIS to RE-route requests to ASP.NET ISAPI filter
This is the last and most critical step to getting this whole exercise to work. We need to tell IIS to pass requests to files with .jpg and .gif extensions to ASP.NET's very own ISAPI filter-aspnet_isapi.dll. To do so, we have to map the extensions to the filter process. This is done in the Application Configuration property sheet of your website or virtual directory.
Pump up the Internet Services Manager, and access your web or virtual directory properties.
- In the Application Settings groupbox, find and click the Configuration button.
- In the next window, select the Mappings tab and click Add to create a new mapping.
- On this dialog, set the Executable to the aspnet_isapi.dll file in the .NET folder (you can just copy this from another .NET extension).
- Then set the Extension to whatever you set in web.config.
- We should sensibly limit this handling only to GET requests for files of the extension in question.
- Press OK and you are done!
Repeat steps 2 to 6 for each extension you wish to handle.

With that out of the way, load up your favorite browser and try accessing any image file, or any page that contains graphics pulled from your website, or a virtual directory on your website, depending on your IIS configuration.
And that's that! Please feel free to contact me at jaison_n_john@hotmail.com if you have any query or feedback to impose on my humble self.
Seasons Greetings to one and all!