Recipe 20.2. Creating a Reusable Image Handler


Problem

You want to create a reusable assembly that retrieves image data from a database and processes it before sending it to a browser.

Solution

Create an HTTP handler to read the image data from the database and send it to the browser.

To implement a custom, reusable HTTP handler:

  1. Create a separate Class Library project in Visual Studio.

  2. Create a class in the project that implements the IHttpHandler interface and then place code to handle the request in the ProcessRequest method.

  3. Compile the project as an assembly and place the assembly in the bin directory of your web project.

  4. Add an <httpHandlers> element to the web.config file in your web project referencing your custom HTTP handler.

  5. Reference the URL of the HTTP handler in your application.

Examples 20-3 and 20-4 show the VB and C# class files we've written to implement an image handler as an HTTP handler. Examples 20-5, 20-6 through 20-7 show the .aspx file and VB and C# code-behind files for our application that demonstrates the use of the HTTP handler.

Discussion

HTTP handlers are classes that implement the IHttpHandler interface. Implementing the IHttpHandler interface requires the implementation of two methods: IsReusable and ProcessRequest. IsReusable is a property that explicitly returns a Boolean value that indicates if the HTTP handler can be reused by other HTTP requests. For synchronous handlers, like our example, the property should always return false so the handler is not pooled (kept in memory). The ProcessRequest method is where the work is performed and you should place code that processes the requests here.

To create a reusable HTTP handler, you need to eliminate all application-specific code from the class. You must compile the class as a separate .NET assembly and place the assembly in the bin directory of each application that uses it.

To create an assembly that contains only the handler code, you need to create a separate Class Library project in Visual Studio. In our example, we have named the project CH20ImageHandlerVB (or CH20ImageHandlerCS for C#), resulting in an assembly that has the same name as the project. We then compile the assembly, place it in the bin directory of the web project, and add a reference to the assembly.

When you create a new Class Library project for your HTTP handler, you will need to add a reference to the System.Web assembly. This is required because the IHttpHandler interface and HttpContext class used by the HTTP handler are defined in the System.Web assembly.


In our example that demonstrates this solution, we have already stored GIF images in a database that can be retrieved and displayed in a browser using our HTTP handler as if they were standard image files. To demonstrate the HTTP handler, we created an ASP.NET page that contains a DropDownList, a View button, and an HTML img tag. The DropDownList displays the descriptions of the images stored in the database. When you make a selection from the list and click the View button, the src attribute for the img tag is set to the URL of our HTTP handler with the ID of the image in the URL. When the page is displayed, the browser requests the image from our HTTP handler, which retrieves the ID of the requested image from the URL, reads the data from the database for the image, and streams the image data to the browser. Figure 20-1 shows the output of the page used to test our HTTP handler.

The image handler implemented in our example needs several pieces of data to retrieve an image from the database. These include the following:

  • The connection string for the database

  • The name of the table containing the image data

  • The name of the column that uniquely identifies an image (the primary key)

  • The name of the column containing the image data

  • A unique identifier (ID) for the image that is to be displayed

To be reusable, none of this data can be coded directly into the handler. To get around this problem in our example, we declare four public constants in the image handler class we can use to specify the names of the variables in Application scope that contain the database information. In addition, the image ID to be downloaded will be passed in the URL used to access the handler (described later).

Figure 20-1. Output from HTTPHandler test page


The application variables defined by the constants are initialized in the Application_Start method of the global.asax class, which is executed when an application is started. If you initialize your application variables in the Application_Start method, they will always be available when HTTP requests are processed. The code to implement this approach is in Examples 20-1 and 20-2.

To create the image handler, you need to create a class that implements IHttpHandler and its two methods, IsReusable and ProcessRequest. Add the code to process requests made to the handler to the ProcessRequest method. As mentioned, IsReusable is a property that returns a Boolean value indicating if the HTTP handler can be reused by other HTTP requests. Because our example is a synchronous handler, the property returns false so the handler is not pooled (kept in memory).

In our example, the first step in processing a request for an image is to get the ID of the requested image from the URL that is being processed by the handler.

Next, a connection to the database needs to be opened. The connection string is obtained from an Application scope variable defined by the APP_CONNECTION_STR constant shown in Examples 20-1 (VB) and 20-2 (C#). The name of the database table, along with the columns containing the unique identifier and the image data, are obtained from the Application scope variables described earlier. These are used to create the SQL statement required to read the image data from the database.

The next step in our example is to read the image data from the database using the ExecuteScalar method of the command object. The ExecuteScalar method returns a generic Object, so the return value must be cast to the type of data stored in the database. In this case, it must be cast to a byte array.

The image data stored in the database for our example is in GIF format, so the content type is set to "image/GIF" to inform the browser of the data type being sent. After setting the content type, the image data is written to the Response object using the BinaryWrite method.

If your image is of another type, you will need to set the ContentType accordingly. Other choices for images include image/jpeg, image/tiff, and image/png.


To use the handler, we have to add information to the <httpHandlers> element of the web.config file of the application to tell ASP.NET which URL requests it should route to our custom image handler. You insert this information using an add element and its attributes. The verb attribute defines the types of requests that are routed to the HTTP handler. The allowable values are *, GET, HEAD, and POST. The value * is a wildcard that specifies that all request types are to be routed to the handler.

The path attribute defines the URL(s) that are to be processed by the HTTP handler. The path can be set to a single URL, or to a less specific value such as *.images to have the HTTP handler process all requests for URLs with an images extension. In our example, we are setting the path to a specific URL (ImageHandlerVB.aspx for the VB example or ImageHandlerCS.aspx for the C# example).

IIS routes requests with the extensions .asax, .ascx, .ashx, .asmx, .aspx, .axd, .config,.cs, .csproj, .lic, .rem, .resources, .resx, .soap, .vb, .vbproj, .vsdisco, and .webinfo to ASP.NET for processing.

To use an HTTP handler for requests with other extensions, IIS must be configured to send the requests with the desired extensions to the aspnet_isapi.dll.


The type attribute defines the name of the assembly and class within the assembly that will process the request in the format type="class name, assembly". The class name must be identified by its full namespace. Here is the code necessary to add a reference to the image handler to an application web.config file:

 

<configuration> <system.web> … <httpHandlers> <add verb="*" path="ImageHandlerVB.aspx" type="ASPNetCookbook.VBExamples.HttpHandlers.ImageHandlerVB, CH20ImageHandlerVB" /> </httpHandlers>

… </system.web> </configuration>

<configuration> <system.web> … <httpHandlers> <add verb="*" path="ImageHandlerCS.aspx" type="ASPNetCookbook.CSExamples.HttpHandlers.ImageHandlerCS, CH20ImageHandlerCS" /> <add verb="*" path="FileDownloadHandlerCS.aspx" type="ASPNetCookbook.CSExamples.HttpHandlers.FileDownloadHandlerCS, CH20FileDownloadHandlerCS"/> </httpHandlers> … </system.web> </configuration>

To use the HTTP handler to retrieve images from the database, we need to set the src attribute of image tags that will use the HTTP handler to the name of the HTTP handler defined in the path attribute of the entry added to web.config, passing the ID of the desired image in the URL. In our example, the src attribute of an img tag is set in the view image button click event of the test page code-behind. Here is a sample URL:

 src="/books/1/505/1/html/2/ImageHandlerVB.aspx?ImageID=13" 

The HTTP handler does not have to be implemented in the same language as the application. The C# image handler can be used in VB projects or vice versa.


See Also

Recipe 15.3; search "Introduction to HTTP Handlers" in the MSDN library

Example 20-1. Application variable initialization for image handler (.vb)

 <%@ Application Language="VB" %> <%@ Import namespace="ASPNetCookbook.VBExamples.HttpHandlers" %> <script RunAt="server"> '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the application start ''' event. It is responsible for initializing application variables. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) Dim strConnection As String 'get the connection string from web.config strConnection = ConfigurationManager. _ ConnectionStrings("dbConnectionString").ConnectionString 'Set application variables used in image HTTP handler example Application.Add(ImageHandlerVB.APP_CONNECTION_STR, strConnection) Application.Add(ImageHandlerVB.APP_IMAGE_TABLE, "BookImage") Application.Add(ImageHandlerVB.APP_IMAGE_ID_COLUMN, "BookImageID") Application.Add(ImageHandlerVB.APP_IMAGE_DATA_COLUMN, "ImageData") End Sub </script> 

Example 20-2. Application variable initialization for image handler (.cs)

 <%@ Application Language="C#" %> <%@ Import namespace="ASPNetCookbook.CSExamples.HttpHandlers" %> <script RunAt="server"> ///*********************************************************************** /// <summary> /// This routine provides the event handler for the application start /// event. It is responsible for initializing application variables. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> void Application_Start(Object sender, EventArgs e) { String strConnection = null; // get the connection string from web.config strConnection = ConfigurationManager. ConnectionStrings["dbConnectionString"].ConnectionString; // Set application variables used in image HTTP handler example Application.Add(ImageHandlerCS.APP_CONNECTION_STR, strConnection); Application.Add(ImageHandlerCS.APP_IMAGE_TABLE, "BookImage"); Application.Add(ImageHandlerCS.APP_IMAGE_ID_COLUMN, "BookImageID"); Application.Add(ImageHandlerCS.APP_IMAGE_DATA_COLUMN, "ImageData"); } // Application_Start </script> 

Example 20-3. Image HTTP handler (.vb)

 Option Explicit On Option Strict On Imports System.Configuration Imports System.Data Imports System.Data.OleDb Imports System.Web Namespace ASPNetCookbook.VBExamples.HttpHandlers ''' <summary> ''' This class provides an image handler as an HTTP handler. ''' </summary> Public Class ImageHandlerVB Implements IHttpHandler 'The following constant is used in the URL used to access this handler to 'define the image required Public Const QS_IMAGE_ID As String = "ImageID" 'The following constants define the name of the application variables 'used to define the database connection string and database table 'information required to retrieve the required image Public Const APP_CONNECTION_STR As String = "DBConnectionStr" Public Const APP_IMAGE_TABLE As String = "DBImageTable" Public Const APP_IMAGE_ID_COLUMN As String = "DBImageIDColumn" Public Const APP_IMAGE_DATA_COLUMN As String = "DBImageDataColumn" '''*********************************************************************** ''' <summary> ''' This property defines whether another HTTP handler can reuse this ''' instance of the handler. ''' </summary> ''' ''' <returns>False</returns> ''' <remarks> ''' False is always returned since this handler is synchronous and is ''' not pooled. ''' </remarks> Public ReadOnly Property IsReusable() As Boolean _ Implements IHttpHandler.IsReusable Get Return (False) End Get End Property 'IsReusable '''*********************************************************************** ''' <summary> ''' This routine provides the processing for the http request. It is ''' responsible for reading image data from the database and writing it ''' to the response object. ''' </summary> ''' ''' <param name="context">Set to the current HttpContext</param> Public Sub ProcessRequest(ByVal context As HttpContext) _ Implements IHttpHandler.ProcessRequest Dim dbConn As OleDbConnection = Nothing Dim dCmd As OleDbCommand = Nothing Dim strConnection As String Dim imageTable As String Dim imageIDColumn As String Dim imageDataColumn As String Dim cmdText As String Dim imageID As String Dim imageData() As Byte Try 'get the ID of the required image from the querystring imageID = context.Request.QueryString(QS_IMAGE_ID) 'get connection string from application scope and open connection 'to the database strConnection = CStr(context.Application(APP_CONNECTION_STR)) dbConn = New OleDbConnection(strConnection) dbConn.Open() 'get the name of the database table and columns where the image 'data is stored then create the SQL to read the data from 'the database imageTable = CStr(context.Application(APP_IMAGE_TABLE)) imageIDColumn = CStr(context.Application(APP_IMAGE_ID_COLUMN)) imageDataColumn = CStr(context.Application(APP_IMAGE_DATA_COLUMN)) cmdText = "SELECT " & imageDataColumn & _   " FROM " & imageTable & _   " WHERE " & imageIDColumn & "=?" dCmd = New OleDbCommand(cmdText, dbConn) dCmd.Parameters.Add(New OleDbParameter("ImageID", _    imageID)) 'get the image data imageData = CType(dCmd.ExecuteScalar(), Byte()) 'write the image data to the reponse object context.Response.ContentType = "image/gif" context.Response.BinaryWrite(imageData) Finally 'clean up If (Not IsNothing(dbConn)) Then dbConn.Close() End If End Try End Sub 'ProcessRequest End Class 'ImageHandlerVB End Namespace 

Example 20-4. Image HTTP handler (.cs)

 using System; using System.Configuration; using System.Data; using System.Data.OleDb; using System.Web; namespace ASPNetCookbook.CSExamples.HttpHandlers { /// <summary> /// This class provides an image handler as an HTTP handler. /// </summary> public class ImageHandlerCS : IHttpHandler { // The following constant is used in the URL used to access this handler // to define the image required public const string QS_IMAGE_ID = "ImageID"; // The following constants defines the name of the application variables // used to define the database connection string and database table // information required to retrieve the required image public const string APP_CONNECTION_STR = "DBConnectionStr"; public const string APP_IMAGE_TABLE = "DBImageTable"; public const string APP_IMAGE_ID_COLUMN = "DBImageIDColumn"; public const string APP_IMAGE_DATA_COLUMN = "DBImageDataColumn"; ///*********************************************************************** /// <summary> /// This property defines whether another HTTP handler can reuse this /// instance of the handler. /// </summary> /// /// <returns>false</returns> /// <remarks> /// false is always returned since this handler is synchronous and is /// not pooled. /// </remarks> public bool IsReusable { get { return (false); } } // IsReusable ///*********************************************************************** /// <summary> /// This routine provides the processing for the http request. It is /// responsible for reading the file from the local filesystem and /// writing it to the response object. /// </summary> /// /// <param name="context">Set to the current HttpContext</param> public void ProcessRequest(HttpContext context) { OleDbConnection dbConn = null; OleDbCommand dCmd = null; String strConnection = null; String imageTable = null; String imageIDColumn = null; String imageDataColumn = null; String strSQL = null; String imageID = null; byte[] imageData = null; try { // get the ID of the required image from the querystring imageID = context.Request.QueryString[QS_IMAGE_ID]; // get connection string from application scope and open connection // to the database strConnection = (String)(context.Application[APP_CONNECTION_STR]); dbConn = new OleDbConnection(strConnection); dbConn.Open(); // get the name of the database table and columns where the image // data is stored then create the SQL to read the data from // the database imageTable = (String)(context.Application[APP_IMAGE_TABLE]); imageIDColumn = (String)(context.Application[APP_IMAGE_ID_COLUMN]); imageDataColumn = (String)(context.Application[APP_IMAGE_DATA_COLUMN]); strSQL = "SELECT " + imageDataColumn +  " FROM " + imageTable +  " WHERE " + imageIDColumn + "=?"; dCmd = new OleDbCommand(strSQL, dbConn); dCmd.Parameters.Add(new OleDbParameter("ImageID",    imageID)); imageData = (byte[])(dCmd.ExecuteScalar()); // write the image data to the reponse object context.Response.ContentType = "image/gif"; context.Response.BinaryWrite(imageData); } // try finally { // clean up if (dbConn != null) { dbConn.Close(); } } // finally } // ProcessRequest } // ImageHandlerCS } 

Example 20-5. Using the image HTTP handler (.aspx)

 <%@ Page Language="VB" MasterPageFile="~/ASPNetCookbookVB.master" AutoEventWireup="false" CodeFile="CH20TestHTTPImageHandlerVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH20TestHTTPImageHandlerVB" Title="Test HTTP Image Handler" %> <asp:Content  runat="server" ContentPlaceHolder> <div align="center" > Test HttpHandler For Images (VB) </div> <table width="50%" align="center" border="0"> <tr> <td> <asp:DropDownList  Runat="server" /> </td> <td> <input  runat="server" type="button" value="View" onserverclick="btnViewImage_ServerClick" /> </td> </tr> <tr> <td  runat="server" colspan="2" align="center" > <br /><br /> Selected Image<br /><br /> <img  runat="server" border="0" alt="book" src="/books/1/505/1/html/2/"/> </td> </tr> </table> </asp:Content> 

Example 20-6. Using the image HTTP handler code-behind (.vb)

 Option Explicit On Option Strict On Imports ASPNetCookbook.VBExamples.HttpHandlers Imports System.Configuration Imports System.Data Imports System.Data.Common Imports System.Data.OleDb Namespace ASPNetCookbook.VBExamples ''' <summary> ''' This class provides the code-behind for ''' CH20TestHTTPImageHandlerVB.aspx ''' </summary> Partial Class CH20TestHTTPImageHandlerVB Inherits System.Web.UI.Page '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the page load event. It ''' is responsible for initializing the controls on the page. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Private Sub Page_Load(ByVal sender As Object, _   ByVal e As System.EventArgs) Handles Me.Load Dim dbConn As OleDbConnection = Nothing Dim dc As OleDbCommand Dim dr As OleDbDataReader Dim strConnection As String Dim strSQL As String If (Not Page.IsPostBack) Then Try 'initially hide the selected image since one is not selected tdSelectedImage.Visible = False 'get the connection string from web.config and open a connection 'to the database strConnection = ConfigurationManager. _ ConnectionStrings("dbConnectionString").ConnectionString dbConn = New OleDbConnection(strConnection) dbConn.Open() 'build the query string and get the data from the database strSQL = "SELECT BookImageID, Title " & _ "FROM BookImage " & _ "ORDER BY Title" dc = New OleDbCommand(strSQL, dbConn) dr = dc.ExecuteReader() 'set the source of the data for the repeater control and bind it ddImages.DataSource = dr ddImages.DataTextField = "Title" ddImages.DataValueField = "BookImageID" ddImages.DataBind() Finally 'clean up If (Not IsNothing(dbConn)) Then dbConn.Close() End If End Try End If End Sub 'Page_Load '''*********************************************************************** ''' <summary> ''' This routine provides the event handler for the view image click ''' event. It is responsible for setting the src attibute of the ''' imgBook tag to the URL of the HTTP handler that will deliver the ''' image content. ''' </summary> ''' ''' <param name="sender">Set to the sender of the event</param> ''' <param name="e">Set to the event arguments</param> Protected Sub btnViewImage_ServerClick(ByVal sender As Object, _  ByVal e As System.EventArgs) 'set the source for the selected image tag imgBook.Src = "ImageHandlerVB.aspx?" & _   ImageHandlerVB.QS_IMAGE_ID & "=" & _   ddImages.SelectedItem.Value.ToString() 'make the selected image visible tdSelectedImage.Visible = True End Sub 'btnViewImage_ServerClick End Class 'CH20TestHTTPImageHandlerVB End Namespace 

Example 20-7. Using the image HTTP handler code-behind (.cs)

 using ASPNetCookbook.CSExamples.HttpHandlers; using System; using System.Configuration; using System.Data; using System.Data.OleDb; namespace ASPNetCookbook.CSExamples { /// <summary> /// This class provides the code-behind for /// CH20TestHTTPImageHandlerCS.aspx /// </summary> public partial class CH20TestHTTPImageHandlerCS : System.Web.UI.Page { ///*********************************************************************** /// <summary> /// This routine provides the event handler for the page load event.  /// It is responsible for initializing the controls on the page. /// </summary>  /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void Page_Load(object sender, EventArgs e) { OleDbConnection dbConn = null; OleDbCommand dc; OleDbDataReader dr; String strConnection; String strSQL; if (!Page.IsPostBack) { try { // initially hide the selected image since one is not selected tdSelectedImage.Visible = false;   // get the connection string from web.config and open a connection // to the database strConnection = ConfigurationManager. ConnectionStrings["dbConnectionString"].ConnectionString; dbConn = new OleDbConnection(strConnection); dbConn.Open(); // build the query string and get the data from the database strSQL = "SELECT BookImageID, Title " + "FROM BookImage " + "ORDER BY Title"; dc = new OleDbCommand(strSQL, dbConn); dr = dc.ExecuteReader(); // set the source of the data for the repeater control and bind it ddImages.DataSource = dr; ddImages.DataTextField = "Title"; ddImages.DataValueField = "BookImageID"; ddImages.DataBind(); } finally { // clean up if (dbConn != null) { dbConn.Close(); } } } } // Page_Load ///*********************************************************************** /// <summary> /// This routine provides the event handler for the view image click /// event. It is responsible for setting the src attibute of the /// imgBook tag to the URL of the HTTP handler that will deliver the /// image content. /// </summary> /// /// <param name="sender">Set to the sender of the event</param> /// <param name="e">Set to the event arguments</param> protected void btnViewImage_ServerClick(Object sender, System.EventArgs e) { // set the source for the selected image tag imgBook.Src = "ImageHandlerCS.aspx?" +   ImageHandlerCS.QS_IMAGE_ID + "=" +   ddImages.SelectedItem.Value.ToString(); // make the selected image visible tdSelectedImage.Visible = true; } // btnViewImage_ServerClick } // CH20TestHTTPImageHandlerCS } 



ASP. NET Cookbook
ASP.Net 2.0 Cookbook (Cookbooks (OReilly))
ISBN: 0596100647
EAN: 2147483647
Year: 2003
Pages: 202

flylib.com © 2008-2017.
If you may any questions please contact us: flylib@qtcs.net