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.
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
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.
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: -
<add verb="" path="" type="" validate=""/>
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
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.
Public Class ImageHandler
Public ReadOnly Property IsReusable() _
As Boolean Implements IHttpHandler.IsReusable
Public Sub ProcessRequest(ByVal context _
As HttpContext) Implements IHttpHandler.ProcessRequest
Dim output As ImageWatermark = _
output.AddWaterMark("This is the custome string")
Private Class ImageWatermark
Private bmp As Bitmap
Public Sub New(ByVal physicalPathToImage As String)
bmp = New Bitmap(physicalPathToImage)
Public Sub AddWaterMark(ByVal watermark As String)
Dim canvas As Graphics
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
New Font("Verdana", 14, FontStyle.Bold), _
New SolidBrush(Color.Beige), 0, 0)
Public ReadOnly Property Image() As Bitmap
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.
<add verb="*" path="*.jpg,*.gif" type="ImageHandler,Home" validate="false"/>
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 firstname.lastname@example.org if you have any query or feedback to impose on my humble self.
Seasons Greetings to one and all!