Documenti di Didattica
Documenti di Professioni
Documenti di Cultura
Introduction
Tools like ASP.NET greatly simplify the development of a complex Web application. Although this is a great thing for general productivity, it can also keep you from
understanding the fundamental communications between your Web server and your ASP.NET application code. Furthermore, there will be times when you need to
butt-in and intercept HTTP requests, which requires a greater understanding of the process of communication between your Web server of choice, and ASP.NET.
This article explains how IIS and ASP.NET communicate, and describes some techniques for intercepting some of this communication. I’ll review how ASP.NET is
configured to handle requests, and how applications and Web services are handled by default.
Then I’ll discuss how you might butt-in to those requests with HTTP handlers, handler factories and modules. You’ll see how they function individually and together
through a series of examples.
When the .NET framework is installed on a machine that has IIS installed, IIS is configured so that requests for specific extensions are handled by aspnet_isapi.dll. As
a point of interest, the filter is also configured within IIS..
Requests for ASP.NET resources are forwarded by IIS to ASP.NET via this configured extension. This extension is the bridge between unmanaged and managed
code. Before control is passed to your application, an ASP.NET application object must be instantiated (by the runtime) and configuration settings are considered to
determine how this request should be handled. Machine.config and collective web.config files are processed collectively to support this process.
For this article, we are specifically interested in the <httpHandlers> configuration section. Settings in this section indicate which .NET type should handle the request.
The default settings found in the machine.config file when .NET is installed, are as follows:
<httpHandlers>
<add verb="*" path="trace.axd" type="System.Web.Handlers.TraceHandler"/>
<add verb="*" path="*.aspx" type="System.Web.UI.PageHandlerFactory"/>
<add verb="*" path="*.ashx" type="System.Web.UI.SimpleHandlerFactory"/>
<add verb="*" path="*.asmx" type="System.Web.Services.Protocols.WebServiceHandlerFactory,
System.Web.Services, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b03f5f7f11d50a3a" validate="false"/>
<add verb="*" path="*.rem"
type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory,
System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" validate="false"/>
<add verb="*" path="*.soap"
type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory,
System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" validate="false"/>
<add verb="*" path="*.asax" type="System.Web.HttpForbiddenHandler"/>
http://www.theserverside.net/tt/articles/content/IIS_ASP/IIS_ASP.html Page 1 / 6
<add verb="*" path="*.ascx" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.cs" type="System.Web.HttpForbiddenHandler"/>
type="System.Runtime.Remoting.Channels.Http.HttpRemotingHandlerFactory,
System.Runtime.Remoting, Version=1.0.3300.0, Culture=neutral,
PublicKeyToken=b77a5c561934e089" validate="false"/>
<add verb="*" path="*.asax" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.ascx" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.config" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.cs" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.csproj" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.vb" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.vbproj" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.webinfo" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.asp" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.licx" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.resx" type="System.Web.HttpForbiddenHandler"/>
<add verb="*" path="*.resources" type="System.Web.HttpForbiddenHandler"/>
<add verb="GET,HEAD" path="*" type="System.Web.StaticFileHandler"/>
<add verb="*" path="*" type="System.Web.HttpMethodNotAllowedHandler"/>
</httpHandlers>
The <httpHandlers> section indicates which HTTP handler factory, or handler, should be used to handle the request. In summary, the high-level workflow from IIS to
your ASP.NET applications is as follows:
•For resources mapped to ASP.NET ISAPI Extension (i.e., *.aspx, *.asmx) the request is passed to an unmanaged ASP.NET DLL which communicates with the
HttpRuntime object
•The HttpRuntime object handles creation of the HttpApplication object (as needed), and the inspection of configuration settings, then passes control to the appropriate
handler for the request
<httpHandlers> settings can be modified at the global (machine.config) level, or overridden at the application (web.config) level. In other words, you can specify a
different factory or handler to process particular resource requests. For example, to reject requests for *.rem objects, you can edit the machine.config, or the
application web.config as follows:
Associating the HttpForbiddenHandler with *.rem replaces the default behavior, which be to use HttpRemotingHandlerFactory. If you specify this override in the
machine.config, this will impact all applications on the Web server.
The following entry associates the System.Web.UI.PageHandlerFactory class with *.aspx resources, for all HTTP verbs (i.e., GET, POST):
These are preexisting classes provided with the base class library, but you can also build custom handlers and handler factories, by implementing the
IHttpHandlerFactory and IHttpHandler interface (respectively). Before we talk about how to implement custom handlers and factories, let’s review their features. From
a high level, HTTP handler factories are specified in order to dynamically return the correct HTTP handler object to manage the requested resource. If an HTTP
handler is specified, it is instantiated directly by the runtime. Regardless, the end-result is to invoke a handler for the resource.
After the runtime passes control to a handler, it is the handler’s job to handle the request, instantiating the appropriate ASP.NET server-side objects, and send an
HTTP response. For example, PageHandlerFactory returns a System.Web.UI.Page object for the requested *.aspx resource, and
System.Web.HttpForbiddenHandler throws an HttpException indicating that the request is not supported.
Implementing IHttpHandler
Similar to ISAPI extensions, handlers provide low-level access to HTTP request and response objects. Implementing a custom handler allows you to process specific
resources differently. You can intercept requests for those resources and override the response. For example, if you wanted to log the IP addresses of those requesting
forbidden files, you could write a handler that logged information about those requests before throwing an HttpException. You may even want to send back a little love
note to those making such request, as shown in the sample code, like this:
To create a custom HTTP handler, create a .NET component that implements IHttpHandler. This interface has the following members:
ProcessRequest() is passed the HttpContext for the request, which can be used to access HttpRequest, HttpResponse and HttpSessionState objects.
NOTE: A handler must implement IRequiresSessionState if it will access the session object.
Here is a simple example of an HTTP handler that writes output to the browser:
context.Trace.Write("ForbiddenLogHandler.ProcessRequest()");
HttpResponse rs = context.Response;
HttpRequest rq = context.Request;
}
http://www.theserverside.net/tt/articles/content/IIS_ASP/IIS_ASP.html Page 3 / 6
public virtual bool IsReusable
{
rs.Write("Your domain is" + rq.UserHostName + "<br>");
rs.Write("<p>Why were you requesting a restricted resource?</p>");
The following web.config section configures the ForbiddenLogHandler for any *.cs, *.resx, or *.config files within the application:
<httpHandlers>
<add verb="*" path="*.cs" type="WebHandlers.ForbiddenLogHandler,WebHandlers"/>
<add verb="*" path="*.resx" type="WebHandlers.ForbiddenLogHandler,WebHandlers"/>
<add verb="*" path="*.config" type="WebHandlers.ForbiddenLogHandler,WebHandlers"/>
</httpHandlers>
Implementing IHttpHandlerFactory
Where HTTP handlers may be useful in responding to requests for specific resources, a handler factory makes it possible to intercept a request, perform some pre-
processing on the request, and then following a factory pattern, create the handler for the resource.
To create a custom handler factory, create a .NET component that implements IHttpHandlerFactory. This interface has the following members:
l GetHandler() – wadda ya know, this method returns a valid IHttpHandler for the runtime to process the requested resource
l ReleaseHandler() – provides an opportunity for the factory to reuse a handler
GetHandler() is called by the runtime, and must return null, or a valid IHttpHandler. Here is an example of an HTTP handler factory that counts the number of hits from
a particular IP address, if the count is exceeded, returns a custom handler, if not, passes control to the default handler for the resource.
if (!HitLogHelper.CheckHitCount(context.Request.UserHostAddress, context.Request.UserHostName))
return new HitsExceededHandler();
try
{
String filename = url.Substring(url.LastIndexOf('/')+1);
String file = filename.Substring(0, filename.IndexOf('.'));
String ext = filename.Substring(filename.LastIndexOf('.')+1);
if (ext == "aspx")
{
return System.Web.UI.PageParser.GetCompiledPageInstance(url, pathTranslated, context);
}
else if (ext == "asmx")
{
System.Web.Services.Protocols.WebServiceHandlerFactory fact = new
System.Web.Services.Protocols.WebServiceHandlerFactory();
}
catch (Exception e)
{
throw new HttpException("HitTrackingHandlerFactory", e);
}
return (IHttpHandler)handler;
}
Implementing IHttpModule
A module provides an event-driven mechanism for interacting with application events, and HTTP request and response objects. Registered modules are instantiated
before HTTP handlers and handler factories, so that they can register for application events. In fact, modules interact with Web applications in two ways: by listening to
application events, and by firing custom events to applications that have registered to listen. The latter of these two tightly couples the module with your application. The
former provides access to the request, response, and session objects at various stages in the processing of each HTTP request, in an independent component.
To create a custom module, create a .NET component that implements IHttpModule. This interface has the following members:
The following sample Init() method registers for the HttpApplication object’s PreRequestHandlerExecute, PostRequestHandlerExecute, and Error events:
You can also handle these events in individual applications in the global.asax, however using a module makes it possible to toggle the configuration externally, and
deploy a common set of event handlers for multiple applications:
<httpModules>
<add name="EventModule" type="WebHandlers.EventModule,WebHandlers"/>
<add name="ErrorHandlerModule" type="WebHandlers.ErrorHandlerModule,WebHandlers"/>
</httpModules>
In such a global error handler, you could provide a catch all error handler that notifies the site administrator of uncaught exceptions.
http://www.theserverside.net/tt/articles/content/IIS_ASP/IIS_ASP.html Page 5 / 6
This next diagram illustrates where custom factories, handlers, modules and extensions would fit in the workflow:
The Code
Code samples supplied with this article demonstrate the order of events when custom handler factories, handlers and modules are all present. Code demonstrates the
following:
l The <trace> element of the web.config file is configured to display trace output in the Web page. When the page is loaded, you’ll be able to see trace
statements, and you can also view the trace.axd file like so:
http://localhost/ASPNETCS/WebResources/trace.axd
l From the main Web page, you can click on invalid resources to see the results of the ForbiddenLogHandler, which handles *.resx, *.cs, and *.config requests.
l A custom message is written to the response stream, but the data returned can also be interesting data to collect from those visiting your site, for data mining.
l The main Web page also has a link to an invalid resource, which will generate an application error. The ErrorHandlerModule is configured to pick up errors, and
e-mail the administrator a notification, while also writing custom HTML to the response stream.
l After 2 passes though the HitTrackingHandlerFactory, you will no longer be able to browse the main page. An XML file is written by the factory, counting hits
from particular IPs, and rejecting that IP after 2 hits. Note the file path is currently hard-coded, so you’ll need to create a directory for the file, and modify the
code to match. Delete the file to reset the behavior. The factory also demonstrates forwarding the request to be handled by default behavior of ASP.NET
configuration, if the request is approved.
Summary
This article should give you the tools you need to determine what architectural model is best for you application, with respect to intercepting HTTP requests, and
modifying application behavior globally. For your reading pleasure, here are a few ideas:
l Customize authentication, to provide a guest account for users that do not have permission
l Pre-authenticate users for a “trial” usage of your application
l Toggle logging behavior, to collect useful statistics about your clients, and perform data mining
http://www.theserverside.net/tt/articles/content/IIS_ASP/IIS_ASP.html Page 6 / 6