You want to create a reusable assembly that retrieves image data from a database and processes it before sending it to a browser.
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:
Create a separate Class Library project in Visual Studio.
Create a class in the project that implements the
IHttpHandler
interface and place code to handle
the request in the ProcessRequest
method.
Compile the project as an assembly and place the assembly in the
bin
directory of your web project.
Add an <httpHandlers>
element to the
web.config
file in your web project referencing
your custom HTTP handler.
Reference the URL of the HTTP handler in your application.
Example 17-3 and Example 17-4 show the VB and C# class files
we’ve written to implement an image handler as an
HTTP handler. Example 17-5 through Example 17-7 show the .aspx
file and
VB and C# code-behind files for our application that demonstrates the
use of the HTTP handler.
HTTP handlers are simply
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
whether 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 actual 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 also 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 VBImageHandler
(or CSImageHandler
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 stored GIF
images in a database that can be retrieved and displayed in a browser
using our HTTP handler just 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 then 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
then streams the image data to the browser. Figure 17-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 that we can use to
specify the names of the variables in Application
scope that contain the database information. In addition, the ID of
the image that is to be downloaded will be passed in the URL used to
access the handler (described later).
The application variables defined by the constants are initialized in
the Application_Start
method of the
global.asax.vb
(VB) or
global.asax.cs
(C#) class, which is executed
when an application is first 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 Example 17-1 and Example 17-2.
Example 17-1. Application variable initialization for image handler (.vb)
Imports ASPNetCookbook.VBExamples.HttpHandlers
Namespace ASPNetCookbook.VBExamples Public Class Global Inherits System.Web.HttpApplication Sub Application_Start(ByVal sender As Object, ByVal e As EventArgs) Dim strConnection As String'get the connection string from web.config
strConnection = _
ConfigurationSettings.AppSettings("dbConnectionString")
'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 'Application_Start End Class 'Global End Namespace
Example 17-2. Application variable initialization for image handler (.cs)
using ASPNetCookbook.CSExamples.HttpHandlers;
namespace ASPNetCookbook.CSExamples { public class Global : System.Web.HttpApplication { protected void Application_Start(Object sender, EventArgs e) { String strConnection = null;// get the connection string from web.config
strConnection =
ConfigurationSettings.AppSettings["dbConnectionString"];
// 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 } // Global }
To create the image handler, you next 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 whether 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 Example 17-1(VB) and Example 17-2 (C#). The name of the database table along
with the columns containing the unique identifier and the image data
are also obtained from the Application
scope
variables described earlier. These are then 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 the GIF
format, so the content type is set to "image/GIF
"
to inform the browser of the type of data 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
“,
"images/tiff
“, and
"images/png
“.
In order to use the handler, we next 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).
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,
VBImageHandler" />
</httpHandlers>
</system.web> </configuration> <configuration> <system.web><httpHandlers>
<add verb="*" path="ImageHandlerCS.aspx"
type="ASPNetCookbook.CSExamples.HttpHandlers.ImageHandlerCS,
CSImageHandler" />
</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. A sample URL is shown here:
src="ImageHandlerVB.aspx?ImageID=5"
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.
Example 17-3. Image HTTP handler (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: ImageHandlerVB.vb ' ' Description: This class provides an image handler as an HTTP handler. ' '***************************************************************************** Imports Microsoft.VisualBasic Imports System.Configuration Imports System.Data Imports System.Data.OleDb Imports System.Web Namespace ASPNetCookbook.VBExamples.HttpHandlers 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"
'*************************************************************************** ' ' ROUTINE: IsReusable ' ' DESCRIPTION: This property defines whether another HTTP handler can ' reuse this instance of the handler. ' ' NOTE: False is always returned since this handler is synchronous ' and is not pooled. '--------------------------------------------------------------------------- Public ReadOnly Property IsReusable( ) As Boolean _ Implements IHttpHandler.IsReusable Get Return (False) End Get End Property 'IsReusable '*************************************************************************** ' ' ROUTINE: ProcessRequest ' ' DESCRIPTION: 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. '---------------------------------------------------------------------------Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
Dim dbConn As OleDbConnection
Dim dCmd As OleDbCommand
Dim strConnection As String
Dim imageTable As String
Dim imageIDColumn As String
Dim imageDataColumn As String
Dim strSQL 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 the connection string and open a 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))
strSQL = "SELECT " & imageDataColumn & _
" FROM " & imageTable & _
" WHERE " & imageIDColumn & "=?"
dCmd = New OleDbCommand(strSQL, 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 17-4. Image HTTP handler (.cs)
//---------------------------------------------------------------------------- // // Module Name: ImageHandlerCS // // Description: This class provides an image handler as an HTTP handler. // //**************************************************************************** using System; using System.Data; using System.Data.OleDb; using System.Web; namespace ASPNetCookbook.CSExamples.HttpHandlers { 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";
//************************************************************************ // // ROUTINE: IsReusable // // DESCRIPTION: This property defines whether another HTTP handler can // reuse this instance of the handler. // // NOTE: false is always returned since this handler is synchronous // and is not pooled. //------------------------------------------------------------------------ public bool IsReusable { get { return(false); } } // IsReusable //************************************************************************ // // ROUTINE: ProcessRequest // // DESCRIPTION: 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. //------------------------------------------------------------------------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 17-5. Using the image HTTP handler (.aspx)
<%@ Page Language="vb" AutoEventWireup="false" Codebehind="CH17TestHTTPImageHandlerVB.aspx.vb" Inherits="ASPNetCookbook.VBExamples.CH17TestHTTPImageHandlerVB" %> <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.0 Transitional//EN"> <html> <head> <title>Test HTTP Image Handler</title> <link rel="stylesheet" href="css/ASPNetCookbook.css"> </head> <body leftmargin="0" marginheight="0" marginwidth="0" topmargin="0"> <form id="frmTestImageHandler" method="post" runat="server"> <table width="100%" cellpadding="0" cellspacing="0" border="0"> <tr> <td align="center"> <img src="images/ASPNETCookbookHeading_blue.gif"> </td> </tr> <tr> <td class="dividerLine"> <img src="images/spacer.gif" height="6" border="0"></td> </tr> </table> <table width="90%" align="center" border="0"> <tr> <td align="center"> </td> </tr> <tr> <td align="center" class="PageHeading"> Test HttpHandler For Images (VB) </td> </tr> <tr> <td><img src="images/spacer.gif" height="10" border="0"></td> </tr> <tr> <td align="center"> <table> <tr> <td><asp:DropDownList ID="ddImages" Runat="server" />
</td> <td><input id="btnViewImage" runat="server"
type="button" value="View">
</td> </tr> <tr><td id="tdSelectedImage" runat="server"
colspan="2" align="center" class="SubHeading">
<br /><br />
Selected Image<br /><br />
<img id="imgBook" runat="server" border="0">
</td>
</tr> </table> </td> </tr> </table> </form> </body> </html>
Example 17-6. Using the image HTTP handler code-behind (.vb)
Option Explicit On Option Strict On '----------------------------------------------------------------------------- ' ' Module Name: CH17TestHTTPImageHandlerVB.aspx.vb ' ' Description: This module provides the code behind for the ' CH17TestHTTPImageHandlerVB.aspx page. ' '***************************************************************************** Imports ASPNetCookbook.VBExamples.HttpHandlers Imports Microsoft.VisualBasic Imports System Imports System.Configuration Imports System.Data Imports System.Data.OleDb Namespace ASPNetCookbook.VBExamples Public Class CH17TestHTTPImageHandlerVB Inherits System.Web.UI.Page 'controls on the form Protected ddImages As System.Web.UI.WebControls.DropDownList Protected WithEvents btnViewImage As _ System.Web.UI.HtmlControls.HtmlInputButton Protected imgBook As System.Web.UI.HtmlControls.HtmlImage Protected tdSelectedImage As System.Web.UI.HtmlControls.HtmlTableCell '*************************************************************************** ' ' ROUTINE: Page_Load ' ' DESCRIPTION: This routine provides the event handler for the page load ' event. It is responsible for initializing the controls ' on the page. '--------------------------------------------------------------------------- Private Sub Page_Load(ByVal sender As System.Object, _ ByVal e As System.EventArgs) Handles MyBase.Load Dim dbConn As OleDbConnection Dim dCmd 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 = _
ConfigurationSettings.AppSettings("dbConnectionString")
dbConn = New OleDbConnection(strConnection)
dbConn.Open( )
'build the query string used to get the data from the database
strSQL = "SELECT BookImageID, Title " & _
"FROM BookImage"
dCmd = New OleDbCommand(strSQL, dbConn)
dr = dCmd.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 '*************************************************************************** ' ' ROUTINE: btnViewImage_ServerClick ' ' DESCRIPTION: 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. '---------------------------------------------------------------------------Private Sub btnViewImage_ServerClick(ByVal sender As Object, _
ByVal e As System.EventArgs) _
Handles btnViewImage.ServerClick
'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 'CH17TestHTTPImageHandlerVB End Namespace
Example 17-7. Using the image HTTP handler code-behind (.cs)
//---------------------------------------------------------------------------- // // Module Name: CH17TestHTTPImageHandlerCS.aspx.cs // // Description: This module provides the code behind for the // CH17TestHTTPImageHandlerCS.aspx page // //**************************************************************************** using ASPNetCookbook.CSExamples.HttpHandlers; using System; using System.Configuration; using System.Data; using System.Data.OleDb; namespace ASPNetCookbook.CSExamples { public class CH17TestHTTPImageHandlerCS : System.Web.UI.Page { protected System.Web.UI.WebControls.DropDownList ddImages; protected System.Web.UI.HtmlControls.HtmlInputButton btnViewImage; protected System.Web.UI.HtmlControls.HtmlImage imgBook; protected System.Web.UI.HtmlControls.HtmlTableCell tdSelectedImage; //************************************************************************ // // ROUTINE: Page_Load // // DESCRIPTION: This routine provides the event handler for the page // load event. It is responsible for initializing the // controls on the page. //------------------------------------------------------------------------ private void Page_Load(object sender, System.EventArgs e) { OleDbConnection dbConn = null; OleDbCommand dc = null; OleDbDataReader dr = null; String strConnection = null; String strSQL = null; // wire the view button click event this.btnViewImage.ServerClick += new EventHandler(this.btnViewImage_ServerClick); 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 =
ConfigurationSettings.AppSettings["dbConnectionString"];
dbConn = new OleDbConnection(strConnection);
dbConn.Open( );
// build the query string and get the data from the database
strSQL = "SELECT BookImageID, Title " +
"FROM BookImage";
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 //************************************************************************ // // ROUTINE: btnViewImage_ServerClick // // DESCRIPTION: 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 page that will // retrieve the image data from the database and stream // it to the browser. // //------------------------------------------------------------------------private 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
} // CH17TestHTTPImageHandlerCS }