Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1282
Chapter 27: Modules and Handlers
The generic format of the httpModules section is
<
httpModules
>
<
add name="modulename" type="namespace.classname, assemblyname" /
>
<
/httpModules
>
If you are deploying your application to an IIS 7 server, you must also add the module configuration to
the <system.webServer> configuration section.
<
modules
>
<
add name="AppendMessage" type="Demo.AppendMessage, App_code"/
>
<
/modules
>
If you have created your HttpModule in the
App_Code
directory of an ASP.NET web site, you might
wonder how you know what the
assemblyname
value should be, considering ASP.NET now dynamically
compiles this code at runtime. The solution is to use the text
App_Code
as the assembly name. This tells
ASP.NET that your module is located in the dynamically created assembly.
You can also create HttpModules as a separate class library in which case you simply use the assembly
name of the library.
After you have added this section to your
web.config
file, simply view one of the Web pages from your
project in the browser. When you view the page in the browser, you should not notice any difference.
But if you view the source of the page, notice the comment you added at the bottom of the HTML.
Figure 27-3 shows what you should see when you view the page source.
Figure 27-3
1282
Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1283
Chapter 27: Modules and Handlers
URL Rewriting
Another interesting use of an HttpModule is to perform URL rewriting. URL rewriting is a technique that
allows you to intercept the HTTP request and change the path that was requested to an alternative one.
This can be very useful for creating pseudo Web addresses that simplify a URL for the user. For example,
the MSDN Library is well known for its extremely long and cryptic URL paths, such as
/>frlrfSystemWebIHttpModuleClassTopic.asp
The problem with this URL is that it is not easy to remember; and even if you do somehow remember it,
it is very difficult to type into the browser’s Address field. URL rewriting allows you to create friendly
URLs that you can parse and redirect to the appropriate resource. The MSDN Library now uses URL
rewriting to create friendly URLs. Instead of the cryptic URL you saw previously, you can now use the
following URL to access the same resource:
/>This URL is much shorter, easier to remember, and easier to type into a browser’s Address field. You can
create your own URL rewriter module to learn how this is done.
To demonstrate this, you create three new Web pages in your project. The first Web page is used to con-
struct a URL using two text boxes. The second serves as the page that accepts the unfriendly querystring,
like the MSDN URL shown previously. The third page is used to trick IIS into helping you serve the
request. Shortly, we talk about this trick and how to get around it.
Listing 27-4 shows the first Web page you add to the project; it’s called
friendlylink.aspx
.
Listing 27-4: The friendlylink.aspx Web page
<
html xmlns=" />>
<
head runat="server"
>
<
title
>
Untitled Page
<
/title
>
<
/head
>
<
body
>
<
form id="form1" runat="server"
>
<
div
>
<
a href="John/Smith/trickiis.aspx"
>
Click this friendly link
<
/a
>
<
/div
>
<
/form
>
<
/body
>
<
/html
>
As you can see, you simply created a hyperlink that links to a friendly, easily remembered URL.
Now, create the second page called
unfriendly.aspx
. This is the page that the handle actually executes
when a user clicks the hyperlink in the
friendlylink.aspx
page. Listing 27-5 shows how to create
unfriendly.aspx
.
Listing 27-5: The unfriendly.aspx Web page
VB
<
%@ Page Language="VB" %
>
Continued
1283
Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1284
Chapter 27: Modules and Handlers
<
script runat="server"
>
Protected Sub Page_Load(ByVal sender As Object, _
ByVal e As System.EventArgs) Handles Me.Load
Label1.Text = Request("firstname").ToString() & _
" " & Request("lastname").ToString()
End Sub
<
/script
>
<
html xmlns=" />>
<
head runat="server"
>
<
title
>
Unfriendly Web Page
<
/title
>
<
/head
>
<
body
>
<
form id="form1" runat="server"
>
<
div
>
Welcome to the unfriendly URL page
<
asp:Label ID="Label1"
runat="server" Text="Label"
><
/asp:Label
>
<
/div
>
<
/form
>
<
/body
>
<
/html
>
C#
<
script runat="server"
>
protected void Page_Load(object sender, EventArgs e)
{
Label1.Text = Request["firstname"].ToString() +
" " + Request["lastname"].ToString();
}
<
/script
>
Next, you create the directory and file that the hyperlink in
friendlyurl.aspx
points to.
The
trickiis.aspx
page can simply be an empty Web page because you are not really going to
execute it.
Finally, you create a new module that parses the request path and rewrites the URL to the page you want
to execute. To do this, create another class in the
App_Code
directory called
SimpleRewriter
. Listing 27-6
shows the code for this.
Listing 27-6: A sample URL rewriting HttpModule
VB
Imports Microsoft.VisualBasic
Imports System.Web
Namespace Demo
Public Class SimpleRewriter
Implements System.Web.IHttpModule
Dim WithEvents _application As HttpApplication = Nothing
Public Overridable Sub Init(ByVal context As HttpApplication) _
Implements IHttpModule.Init
_application = context
End Sub
1284
Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1285
Chapter 27: Modules and Handlers
Public Overridable Sub Dispose() Implements IHttpModule.Dispose
End Sub
Public Sub context_BeginRequest(ByVal sender As Object, _
ByVal e As EventArgs) Handles _application.BeginRequest
Dim requesturl As String = _
_application.Context.Request.Path.Substring(0, _
_application.Context.Request.Path.LastIndexOf("/"c))
’Here is where we parse the original request url to determine
’ the querystring parameters for the unfriendly url
Dim parameters() As String = _
requesturl.Split(New [Char]() {"/"c}, _
StringSplitOptions.RemoveEmptyEntries)
If (parameters.Length
>
1) Then
Dim firstname As String = parameters(1)
Dim lastname As String = parameters(2)
’Rewrite the request path
_application.Context.RewritePath("~/unfriendly.aspx?firstname=" & _
firstname & "&lastname=" & lastname)
End If
End Sub
End Class
End Namespace
C#
using System.Web;
namespace Demo
{
public class SimpleRewriter: System.Web.IHttpModule
{
HttpApplication _application = null;
public void Init(HttpApplication context)
{
context.BeginRequest+=new System.EventHandler(context_BeginRequest);
_application = context;
}
public void Dispose()
{
}
private void context_BeginRequest(object sender, System.EventArgs e)
{
string requesturl =
_application.Context.Request.Path.Substring(0,
Continued
1285
Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1286
Chapter 27: Modules and Handlers
_application.Context.Request.Path.LastIndexOf("//")
);
//Here is where we parse the original request url to determine
//the querystring parameters for the unfriendly url
string[] parameters = requesturl.Split(new char[] {’/’});
if (parameters.Length
>
1)
{
string firstname = parameters[1];
string lastname = parameters[2];
//Rewrite the request path
_application.Context.RewritePath("~/unfriendly.aspx?firstname=" +
firstname + "&lastname=" + lastname);
}
}
}
}
As you can see from the listing, in this sample you use the BeginRequest event in the HttpModule to
parse the incoming HTTP request path and create a new URL that you execute. Normally, when you
click the hyperlink on
friendlyurl.aspx
, an HTTP request is sent to the server for execution and then
IIS returns the page asked for in the hyperlink. In this case, you make a request for this page:
http://localhost:1234/WebProject1/John/Smith/trickiis.aspx
But, because you put the HttpModule in the request-processing pipeline, you can modify the HTTP
request and change its behavior. The code in the
BeginRequest
method of the module parses the request
path to create a querystring that the
unfriendly.aspx
page can understand and execute. So when you
execute the code in the listing, you convert the original path into the following:
http://localhost:1234/WebProject1/unfriendly.aspx?var1=John&var2=Smith
This URL is, as the page name states, not very friendly; and the user is less likely to remember and be
able to type this URL. Finally, the module uses the
RewritePath
method to tell ASP.NET that you want
to rewrite the path to execute for this request.
After you have completed creating the code for this sample, try loading
friendlylink.aspx
into a
browser. When you click the hype rlink on the page, you should notice two things. First, notice that the
URL in the browser’s address bar shows that you have been served the page you requested,
trickiis.aspx
, but the contents of the page show that you are actually served
unfriendly.aspx
.
Figure 27-4 shows what the browser looks like.
IIS WildCards
There is, however, a drawback to this type of URL rewriting. When IIS receives a request to serve a
resource, it first checks to see if the resource exists. If the resource does exist, the request is passed to the
appropriate handler; in this case, the handler is ASP.NET, which processes the request. IIS then returns
the results to the client. However, if the requested resource does not exist, IIS returns a
404 File Not
Found
error to the client. It never hands the request to ASP.NET. In order for a module to execute, you
must create an endpoint that actually exists on the Web server.
1286
Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1287
Chapter 27: Modules and Handlers
Figure 27-4
In the case of t he previous example, you had to actually create the
/WebProject1/John/Smith/
trickiis.aspx
path so that IIS would know which handler to give the request to. Had you not cre-
ated the path, IIS would have simply returned a 404 e rror. Unfortunately, having to create the physical
paths can be a problem if you find you are creating a large number of directories or resources just to
enable URL rewriting. Thankfully, however, a solution to this problem exists in IIS: wildcards. Wildcards
allow you to tell IIS that it should use a particular handler to process all incoming requests, regardless of
the requested paths or file extensions. In the previous example, adding a w ildcard would keep you from
having to actually create the dummy end point for the module.
Adding Wildcards in IIS 5
To add a wildcard mapping in IIS 5, start by opening the IIS Management Console. After the console
is open, right-click on the Web site or virtual directory you want to modify and select the Properties
option from the context menu. When the Properties dialog box opens, select the Configuration button
from the Home Directory Tab. The Configuration dialog is where you can create new or modify existing
application extension mappings. If you take a moment to look at the e xisting mappings, you see that
most of the familiar file extensions (such as
.aspx
,
.asp
,and
.html
) are configured.
To add the wildcard that you need to create a new mapping, click the Add button. The Add Application
Extension Mapping dialog box is shown in Figure 27-5. You create a mapping that directs IIS to use the
ASP.NET ISAPI DLL to process every incoming request. To do this, simply put the full path to the ISAPI
DLL (usually something such as
C:
\
WINDOWS
\
Microsoft.NET
\
Framework
\
v1.1.4322
\
aspnet_isapi.dll
)
in the Executable field. For the extension, simply use .*, which indicates any file extension.
Additionally, you uncheck the Check That File Exists check box to tell IIS not to check whether the
requested file exists before processing (because you know that it doesn’t).
Now you don’t have to add the stub file to your Web site. IIS will pass any request that it receives to
ASP.NET for processing, regardless of whether the file exists.
Adding Wildcards in IIS 6
Adding Wildcards in IIS 6 is similar to adding wildcards in IIS 5. Open the IIS Management Console, and
then open the Properties dialog box for the Web site or virtual directory you want to modify. Next, click
the Configuration button on the Home Directory tab.
1287
Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1288
Chapter 27: Modules and Handlers
Figure 27-5
The Application Extension Configuration dialog in IIS is slightly different. Wildcard application maps
now have their own separate listing, as shown in Figure 27-6.
To add a wildcard mapping, click the Insert button, add the path to the ASP.NET ISAPI DLL, and make
sure the Verify That File Exists check box is unchecked.
Figure 27-6
1288
Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1289
Chapter 27: Modules and Handlers
HttpHandlers
HttpHandlers differ from HttpModules, not only because of their positions in the request-processing
pipeline (refer to Figure 27-3), but also because they must be mapped to a specific file e xtension. Handlers
are the last stop for incoming HTTP requests and are ultimately the point in the request-processing
pipeline that is responsible for serving up the requested content, be it an ASPX page, HTML, plain text,
or an image. Additionally, HttpHandlers can offer significant performance gains.
In this section, we demonstrate two different ways to create a simple HttpHandler that you can use to
serve up dynamic images. First, you look at creating an HttpHandler using an ASHX file extension.
Then you learn how you get even more control by mapping your HttpHandler to a custom file extension
using IIS.
Generic Handlers
In early versions of Visual Studio, HttpHandlers were somewhat hard to understand and create because
little documentation was included to help developers understand handlers. In addition Visual Studio did
not provide any friendly methods for creating them.
Since Visual Studio 2005 however this has changed with Visual Studio now providing a standard
template for HttpHandlers to help you get started. To add an HttpHandler to your project, you
simply select the Generic Handler file type from the Add New Item dialog. Figure 27-7 shows this dialog
box with the file type selected.
Figure 27-7
1289
Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1290
Chapter 27: Modules and Handlers
You can see that when you add t he Generic Handler file to your project, it adds a file with an
.ashx
extension. The
.ashx
file extension is the default HttpHandler file extension set up by ASP.NET. Remem-
ber that HttpHandlers must be mapped to a unique file extension, so by default ASP.NET uses the
.ashx
extension. This is convenient because, otherwise, you would be responsible for adding the file extension
yourself. This is obviously not always possible, nor is it practical. Using the Custom Handler file type
helps you avoid any extra configuration.
Notice the class stub that the file type automatically creates f or you. Listing 27-7 shows the class.
Listing 27-7: The HttpHandler page template
VB
<
%@ WebHandler Language="VB" Class="Handler" %
>
Imports System.Web
Public Class Handler
Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
context.Response.ContentType = "text/plain"
context.Response.Write("Hello World")
End Sub
Public ReadOnly Property IsReusable() As Boolean _
Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
C#
<
%@ WebHandler Language="C#" Class="Handler" %
>
using System.Web;
public class Handler : IHttpHandler {
public void ProcessRequest (HttpContext context) {
context.Response.ContentType = "text/plain";
context.Response.Write("Hello World");
}
public bool IsReusable {
get {
return false;
}
}
}
1290
Evjen c27.tex V2 - 01/28/2008 3:51pm Page 1291
Chapter 27: Modules and Handlers
Notice that the stub implements the IHttpHandler interface, which requires the
ProcessRequest
method
and
IsReusable
property. The
ProcessRequest
method is the method we use to actually process the
incoming HTTP request. By default, the class stub changes the content type to plain and then writes
the
"Hello World"
string to the output stream. The
IsReusable
property simply lets ASP.NET know if
incoming HTTP requests can reuse the sample instance of this HttpHandler.
By default, this handler is ready to run right away. Try executing the handler in your browser and see
what happens. The interesting thing to note about this handler is that because it changes the content to
text/plain, browsers handle the responses from this handler in potentially very different ways depending
on a number of factors:
❑ Browser type and version
❑ Applications loaded on the system that may map to the MIME type
❑ Operating system and service pack level
Based on these factors, you might see the text returned in the browser, you might see Notepad open and
display the text, or you might receive the Open/Save/Cancel prompt from IE. Make sure you understand
the potential consequences of changing the ContentType header.
You can continue the example by modifying it to return an actual file. In this case, you use the handler
to return an image. To do this, you simply modify the code in the
ProcessRequest
method,asshownin
Listing 27-8.
Listing 27-8: Outputting an image from an HttpHandler
VB
<
%@ WebHandler Language="VB" Class="Handler" %
>
Imports System.Web
Public Class Handler : Implements IHttpHandler
Public Sub ProcessRequest(ByVal context As HttpContext) _
Implements IHttpHandler.ProcessRequest
’Logic to retrieve the image file
context.Response.ContentType = "image/jpeg"
context.Response.WriteFile("Garden.jpg")
End Sub
Public ReadOnly Property IsReusable() As Boolean _
Implements IHttpHandler.IsReusable
Get
Return False
End Get
End Property
End Class
C#
<
%@ WebHandler Language="C#" Class="Handler" %
>
Continued
1291