Wednesday, April 04, 2007 4:12 AM bart

Extending IIS 7.0 - Part 2: Developing an HTTP handler for image processing

Note: This post is part of the follow-up for the Developer & IT Pro Days 2007 session entitled "Internet Information Services (IIS) 7.0 - End-to-End Overview of Microsoft's New Web Application Server". Attendees will be able to download the slides and will get the video recording of the entire session as well. Using these posts, attendees should be capable of reproducing the entire demo part of the session. Furthermore, others who didn't attend the session will be able to get a better idea of what IIS 7.0 provides by following the demo steps outlined in these posts. In order to implement the demos, one should have IIS 7.0 installed on Windows Vista (or Windows "Longhorn" Server).



In the previous part of this series, we covered the steps to create an HTTP module for logging. In this post, another extensibility mechanism in IIS 7.0 is put in the spotlights: HTTP handlers. Basically, an HTTP handler serves a request, providing a response to it. The handler is called by means of some mapping mechanism, for example by mapping every request with extension .jpg on an appropriate handler.

For sake of the demo, we'll build a simple handler that serves .jpg file requests, allowing images to be resized dynamically. On top of this, we'll also provide functionality to put image copyright messages on top of an image, as explained in " - End-to-End Extensibility Example". All credits for the image copyright functionality go to the authors Michael Jurek and Thomas Deml. In this and future posts however, we'll take a slightly different approach at various places in order to make things easier or in order to make the explanation more clear.


Building the image handler from A to Z

Right, let's get started. In this post, we assume you've followed at least steps 1 to 5 from the logging demo of the previous post. Ideally, you've completed the complete demo concerning the HTTP logging module first.

  1. In Visual Studio 2005, right-click the devdays project's App_Code subfolder and add a new class file by clicking Add New Item... and supplying the name PictureHandler.cs:

  2. Get rid of non-required imports at the top of the class file, keeping the following ones:
    using System; using System.Web;

  3. Implement the IHttpHandler interface, like this:

  4. Now it's time to write the code for the handler itself; we'll start by providing the implementation to the IsReusable property getter:
    public bool IsReusable { get { return true; } }

    This indicates whether or not the system can reuse an instance of the handler across multiple requests. We don't bother in this case, so returning true is just fine.

  5. Next, the real implementation is to be supplied inside ProcessRequest. We'll start by implementing the class trivially, like this:

    public void ProcessRequest(HttpContext context) { string path = context.Request.PhysicalPath; context.Response.WriteFile(path); }

    This is the managed code equivalent to a static file serving handler, that doesn't care about any error codes whatsoever (think of the 404 case for instance). Nevertheless, it suits our demo purpose for now.
  6. We're ready to test the dummy handler implementation. To do so compile the solution by pressing CTRL-SHIFT-B and go to the IIS Manager. Navigate to the devdays application underneath Web Sites, Default Web Site and open up the Handler Mappings configuration section:


    From the actions pane on the right, choose Add Manager Handler... and supply *.jpg (don't forget the *) for the Request path, PictureHandler for the Type (which is a combo box) and PictureHandler as the Name (used to reference the handler, it can - but shouldn't necessarily - have the same name as the type):


    Next, click Request Restrictions and check the "Invoke handler only if request is mapped to" checkbox on the Mapping tab to serve only File requests. This rules out requests occurring to non-existing files or requests to folders being served by the handler:

    On the Verbs tab, select the "One of the following verbs" radio button and specify GET in the textbox below:

    Click OK twice to add the handler:

  7. Switch back to Visual Studio 2005 and observe that a change to web.config was seen by the IDE:

    In case you didn't have a web.config file yet (which is the case if you didn't complete any other steps than steps 1-5 from the previous post), one will be created. Anyhow, the web.config should now contain something like this:
    <system.webServer> <modules> <add name="Logger" type="HttpLogger" /> </modules> <handlers> <add name="PictureHandler" path="*.jpg" verb="GET" type="PictureHandler" resourceType="File" /> </handlers> </system.webServer>

  8. Go to Windows Explorer (elevated as an administrator, see previous post) and copy a .jpg picture to the devdays application folder, for instance the Waterfall.jpg file from %SystemDrive%\Users\Public\Pictures\Sample Pictures:

  9. Open the browser and make a request to http://localhost/devdays/waterfall.jpg to make sure the file is still served. If you receive an error, make sure you've compiled the web site in Visual Studio 2005:

  10. In order to convince yourself that the managed code handler is effectively serving the image (instead of IIS's default static file handler), change the ProcessRequest method as follows:
    public void ProcessRequest(HttpContext context) { throw new Exception("Bang!"); string path = context.Request.PhysicalPath; context.Response.WriteFile(path); }

    Recompile, go back to the browser and hit refresh. You should see the following coming up:

  11. Switch back to Visual Studio 2005 and change the ProcessRequest like this:
    1 public void ProcessRequest(HttpContext context) 2 { 3 string path = context.Request.PhysicalPath; 4 5 Image img = Bitmap.FromFile(path); 6 7 string p = context.Request.QueryString["percent"]; 8 int percent; 9 if (p != null && int.TryParse(p, out percent) && percent > 0 && percent < 100) 10 { 11 Image t = img; 12 img = new Bitmap(img.GetThumbnailImage(img.Width * percent / 100, img.Height * percent / 100, null, IntPtr.Zero)); 13 t.Dispose(); 14 } 15 16 string msg = "Copyright Microsoft (C) 2007"; 17 18 using (Graphics g = Graphics.FromImage(img)) 19 { 20 Font f = new Font("Arial", 10, FontStyle.Bold); 21 SizeF s = g.MeasureString(msg, f); 22 PointF pos = new PointF(img.Width - s.Width - 5, img.Height - s.Height - 5); 23 g.DrawString(msg, f, Brushes.Yellow, pos); 24 } 25 26 img.Save(context.Response.OutputStream, ImageFormat.Jpeg); 27 img.Dispose(); 28 }

    In order to have this compile properly, import the namespaces System.Drawing and System.Drawing.Imaging:
    using System.Drawing; using System.Drawing.Imaging;

    Note: The calls to Dispose() and equivalent using-blocks wasn't shown during the demo on Dev & IT Pro Days 2007 in order to make the code size more manageable by the audience. However, to avoid leaks caused by non-proper disposal of GDI objects, these are required as shown above.

    This piece of code deserves some explanation:
    1. One line 5, the bitmap is retrieved from the web server's disk.
    2. Lines 7 to 14 handle the resizing case, allowing users to make a request to the jpg file with a percent querystring parameter. TryParse is used to make sure the parameter is a valid integer and subsequent conditions take care of a valid resize percentage (you could change this to allow zooming as well).
    3. The trick on line 12 is required to make a new bitmap representing the resized one. Don't bother too much about the null, IntPtr.Zero plumbing in the GetThumbnailImage call.
    4. Line 13 disposes of the image that was used to so far, before the resizing happened.
    5. Lines 18 to 24 draw the msg string containing copyright info to the bottom right corner of the picture. You could change this logic to use a different font or draw to another position of the image. Notice that the code doesn't care about images that are too small to fit the string.
    6. The using block on line 18 takes care of proper disposal of the graphical context object.
    7. Line 26 saves the created image directly to the output stream of the response, in the specified Jpeg format.
    8. Line 27 takes care of proper disposal of the current image object. Notice you can't use a using-block for the img variable starting from line 5 since an assignment to the variable happens on line 12. A CS1656 compilation error would result, telling you "Cannot assign to 'img' because it is a 'using variable'"
    9. The code can be improved by leveraging the ASP.NET caching power but this falls out of the scope of this demo.
  12. Recompile the project and switch back to the browser. Make a request to waterfall.jpg, making sure that the image copyright message is displayed:

    Make a request with a percentage parameter too, like this:


The code for this post can be downloaded over here. In the next post in this series, we'll take a closer look at the configuration system of IIS 7.0. | Digg It | Technorati | Blinklist | Furl | reddit | DotNetKicks

Filed under:


# Extending IIS 7.0 - Part 3: Extending and using the configuration system

Wednesday, April 04, 2007 3:04 PM by B# .NET Blog

Note: This post is part of the follow-up for the Developer &amp; IT Pro Days 2007 session entitled "Internet

# Extending IIS 7.0 - Part 3: Extending and using the configuration system

Wednesday, April 04, 2007 3:34 PM by B# .NET Blog

Note: This post is part of the follow-up for the Developer &amp; IT Pro Days 2007 session entitled "Internet

# re: Extending IIS 7.0 - Part 2: Developing an HTTP handler for image processing

Tuesday, April 10, 2007 8:07 AM by Matthew

Great example, thanks for posting it.

One minor tweak -- to get this to work with Firefox as the browser I had to specify a ContentType on the response object:

context.Response.ContentType = "image/jpeg";

# Form Authentication e File statici | hilpers

Sunday, January 18, 2009 5:04 AM by Form Authentication e File statici | hilpers

Pingback from  Form Authentication e File statici | hilpers