Implementing Intercepting Filter in ASP.NET Using HTTP Module
Retired Content |
---|
This content is outdated and is no longer being maintained. It is provided as a courtesy for individuals who are still using these technologies. This page may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. Please see the patterns & practices guidance for the most current information. |
Version 1.0.1
GotDotNet community for collaboration on this pattern
Complete List of patterns & practices
Context
You are building a Web application in Microsoft ASP.NET with many different types of requests. Some requests are forwarded to the appropriate page, and others must be logged or modified in some way before being processed.
Implementation Strategy
The ASP.NET implementation of the Intercepting Filter pattern is an example of the event-driven filters described in the pattern. ASP.NET provides a series of events during request processing that your application can hook into. These events guarantee the state of the request. Individual filters are implemented with an HTTP module. An HTTP module is a class that implements the IHttpModule interface and determines when the filter should be called. ASP.NET includes a set of HTTP modules that can be used by your application. For example, SessionStateModule is provided by ASP.NET to supply session state services to an application. You can create your own custom HTTP modules to filter the request or response as needed by your application.
The general process for writing a custom HTTP module is:
Implement the IHttpModule interface.
Handle the Init method and register for the events you need.
Handle the events.
Optionally, implement the Dispose method if you have to do cleanup.
Register the module in the web.config file.
Events
The following lists show the events that, when raised during the processing of a request, can be intercepted using ASP.NET. All events are listed in the order in which they occur.
The first list shows the events that are raised before the request is processed:
BeginRequest. This event signals a new request; it is guaranteed to be raised on each request.
AuthenticateRequest. This event signals that the configured authentication mechanism has authenticated the request. Attaching to this event guarantees your filter that the request has been authenticated.
AuthorizeRequest. Like AuthenticateRequest, this event signals that the request is now one step further down the chain and has been authorized.
ResolveRequestCache. The output cache module uses this event to short-circuit the processing of requests that have been cached.
AcquireRequestState. This event signals that individual request state should be obtained.
PreRequestHandlerExecute. This event signals that the request handler is about to execute. This is the last event you can participate in before the HTTP handler for this request is called.
The next list shows the events that are raised after the request is processed. The events are listed in the order in which they occur:
PostRequestHandlerExecute. This event signals that the HTTP handler has finished processing the request.
ReleaseRequestState. This event signals that the request state should be stored because the application is finished with the request.
UpdateRequestCache. This event signals that code processing is complete and the file is ready to be added to the ASP.NET cache.
EndRequest. This event signals that all processing has finished for the request. This is the last event called when the application ends.
In addition, the following three per-request events can fire in a nondeterministic order:
PreSendRequestHeaders. This event signals that HTTP headers are about to be sent to the client. This provides an opportunity to add, remove, or modify the headers before they are sent.
PreSendRequestContent. This event signals that content is about to be sent to the client. This provides an opportunity to modify the content before it is sent.
Error. This event signals an unhandled exception.
The following example demonstrates how a request is intercepted after it has been authenticated by the ASP.NET runtime. When the example module, called UserLogger, is initialized, it connects a member function, called OnAuthenticate, to the AuthenticateRequest event. Every time a new request is authenticated, the OnAuthenticate function is called. In this example, the OnAuthenticate function logs the name of the authenticated user to the Intercepting Filter Pattern application event log.
using System;
using System.Web;
using System.Security.Principal;
using System.Diagnostics;
public class UserLogModule : IHttpModule
{
private HttpApplication httpApp;
public void Init(HttpApplication httpApp)
{
this.httpApp = httpApp;
httpApp.AuthenticateRequest += new EventHandler(OnAuthentication);
}
void OnAuthentication(object sender, EventArgs a)
{
HttpApplication application = (HttpApplication)sender;
HttpResponse response = application.Context.Response;
WindowsIdentity identity =
(WindowsIdentity)application.Context.User.Identity;
LogUser(identity.Name);
}
private void LogUser(String name)
{
EventLog log = new EventLog();
log.Source = "Intercepting Filter Pattern";
log.WriteEntry(name,EventLogEntryType.Information);
}
public void Dispose()
{}
}
The example module must be added to the web.config file so that the ASP.NET runtime recognizes the module. The following is the configuration file that changes for the UserLogModule example module:
<httpModules>
<add name="UserLogModule" type="UserLogModule, ifilter" />
</httpModules>
Examples
The following are examples of intercepting filters that are built into Microsoft .NET:
DefaultAuthenticationModule. This filter ensures that an Authentication object is present in the HttpContext object.
FileAuthorizationModule. This filter verifies that the remote user has Microsoft Windows NT permissions to access the file requested.
FormsAuthenticationModule. This filter enables ASP.NET applications to use forms authentication.
PassportAuthenticationModule. This filter provides a wrapper around PassportAuthentication services for Passport authentication.
SessionStateModule. This filter provides session-state services for an application.
UrlAuthorizationModule. This filter provides URL-based authorization services for allowing or denying access to specified URLs.
WindowsAuthenticationModule. This filter enables ASP.NET applications to use Microsoft Windows or Internet Information Services (IIS) authentication.
Testing Considerations
Testing the HTTP modules without the ASP.NET runtime is not possible. Therefore, a slightly different implementation strategy must be employed to separate as much of the functionality as possible from the class that implements the IHttpModule interface. In the previous example, the code that logs the user name does not require the ASP.NET runtime. This functionality can be placed in its own class, called UserLog, which is independent of ASP.NET. The UserLogAdapter class, which implements the IHttpModule interface, can use the UserLog class. This enables other classes to use the UserLog class and also enables you to test it without the ASP.NET environment. The following is the same functionality as described previously, but implemented in a way that allows the logging functionality to be tested without the ASP.NET runtime:
using System;
using System.Diagnostics;
public class UserLog
{
public static void Write(String name)
{
EventLog log = new EventLog();
log.Source = "Intercepting Filter Pattern";
log.WriteEntry(name,EventLogEntryType.Information);
}
}
using System;
using System.Web;
using System.Security.Principal;
public class UserLogAdapter
{
private HttpApplication httpApp;
public void Init(HttpApplication httpApp)
{
this.httpApp = httpApp;
httpApp.AuthenticateRequest += new EventHandler(OnAuthentication);
}
void OnAuthentication(object sender, EventArgs a)
{
HttpApplication application = (HttpApplication)sender;
HttpResponse response = application.Context.Response;
WindowsIdentity identity =
(WindowsIdentity)application.Context.User.Identity;
UserLog.Write(identity.Name);
}
public void Dispose()
{}
}
Resulting Context
The implementation of the Intercepting Filter pattern results in the following benefits and liabilities:
Benefits
Uses event-driven filters. The ASP.NET runtime provides numerous events, which enable the programmer to hook into the right place to add their functionality. This is beneficial, because they can assume the current state of the request based on the event. For example, if the event is AuthenticateRequest, you can assume that the request is authenticated prior to your filter being called.
Enables flexible configuration. The modules are added or removed by editing the web.config file. The source code does not have to be changed, and the ASP.NET runtime does not have to be restarted.
Alleviates order dependency. One of the liabilities of Intercepting Filter is that filters should not be order-dependent. Because the ASP.NET implementation uses events, it alleviates the problem by using events to indicate that certain processing has occurred.
Liabilities
Testing of classes that implement the IHttpModule interface is difficult or impossible without testing the full ASP.NET runtime.
Related Patterns
For more information, see Adapter [Gamma95]. The Adapter pattern was used in "Testing Considerations" to help isolate the core functionality and to enhance testability.
Acknowledgments
[Gamma95] Gamma, Helm, Johnson, and Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995.