Intercepting FilterVersion 1.0.1 GotDotNet community for collaboration on this pattern Complete List of patterns & practices
Context Anyone who has built a Web application from scratch realizes that it requires bit more housekeeping work than building an internal client-server application. First, you have to deal with the HTTP and all its quirks such as HTTP headers, multi-part forms, the statelessness of HTTP, character set encoding schemes, Multipurpose Internet Mail Extensions (MIME) types, and URL rewriting. On top of that, you have to deal with security measures such as Secure Sockets Layer (SSL) and user authentication. In many situations, the list continues on to include such items as client browser detection or user activity logging. Web application server frameworks perform many of these tasks for you, but sometimes you need additional control, or you need to insert your own processing steps before or after the application processes the Web page request. Problem How do you implement common pre- and post-processing steps around Web page requests? Forces There are many ways to approach this problem, so you will need to consider what forces and tradeoffs are involved: Solution Create a chain of composable filters to implement common pre-processing and post-processing tasks during a Web page request.
The filters form a series of independent modules that can be chained together to execute a set of common processing steps before the page request is passed to the controller object. Because the individual filters implement identical interfaces, they do not have explicit dependencies on each other. Therefore, new filters can be added without affecting existing filters. You can even add filters at deployment time by instantiating them dynamically based on a configuration file.
As much as possible, you should design the individual filters in such as way that they make no assumptions about the presence of other filters. This maintains the composability; that is, the ability to add, remove, or rearrange filters. Also, some frameworks that implement the Intercepting Filter pattern do not guarantee the order in which the filters are executed. If you find that you have strong interdependencies between multiple filters, a regular method with calls to helper classes may be the better choice because it guarantees to preserve the constraints in the filters sequence.
In some contexts, the term Intercepting Filter is associated with a specific implementation using the Decorator pattern [Gamma95]. The solution described here takes a bit more abstract view and considers different implementation options of the Intercepting Filter concept.
Filter Chain
A straightforward implementation of Intercepting Filter is a filter chain that iterates through a list of all filters. The Web request handler executes the filter chain before passing control to the application logic (see Figure 2).
When the Web server receives a page request, Request Handler passes control to the FilterChain object first. This object maintains a list of all filters and calls each filter in sequence. FilterChain can read the sequence of filters from a configuration file to achieve deployment-time composability. Each filter has the chance to modify the incoming request. For example, it can modify the URL or add header fields to be used by the application. After all filters have been executed, Request Handler passes control to the controller, which executes the application functionality (see Figure 3).
One of the key benefits of this design is that filters are self-contained components without any direct dependency on the other filters or the controller, because FilterChain invokes each filter. Therefore, a filter does not have to hold a reference to the next filter. The handler passes a context into each filter on which the filter operates. The filter can manipulate the context, for example, by adding information or redirecting the request.
Decorator
An interesting alternative implementation to the Intercepting Filter pattern uses the Decorator pattern around a Front Controller. Decorator wraps an object in such a way that it provides the same interface as the original object. As a result, the wrapping is transparent to any other object that references the original object. Because the interface of the original object and wrapper are identical, you can add additional wrappers around the wrapper to create a chain of wrappers that is very similar to a filter chain. Inside each wrapper, you can perform pre-processing and post-processing functions.
Figures 4 and 5 show how this concept can be used to implement Intercepting Filter. Each filter implements the Controller interface. It also holds a reference to the next object that implements the Controller interface, which could be either the actual controller (concreteController) or another filter. Even though the filters call each other directly, there is no direct dependency between the filters, because each filter only references the Controller interface instead of the next filter class.
Before the filter passes control to the next filter, it has the opportunity to perform pre-processing tasks. Likewise, after the rest of the chain is finished processing the request, the filter has an opportunity to perform post-processing tasks.
The Decorator approach avoids the need for a FilterChain class that iterates over the filters. Also, the request handler is now completely unaware of the existence of the filters. As far as the request handler is concerned, it simply calls the controller by using the Controller interface. This approach usually appears more elegant to hardcore object-oriented developers, but it can be a bit more difficult to figure out what is going on by looking at the code. The Decorator approach relates to the Filter Chain approach much as a linked list relates to an array with an iterator.
Even though the object instances have references to each other, you can still compose the chain at runtime. You can instantiate each filter passing along a reference to the Controller interface of the next filter object in the chain. That way, you can build the filter chain dynamically from back to front.
Event-Driven Filters
In an ideal world, you would design the individual filters in such a way that they were not dependent on the sequence in which they were executed, but the real world rarely works that way. Even if you manage to design the filters independently, they will end up replicating a lot of functionality. For example, each filter that has to analyze the HTTP headers (for example, to do browser detection and extract cookies) will have to parse the headers, extract the header element names, and perform some action on them. It would be much easier if the framework could do some of this work and pass along a collection of all header elements, validated and indexed by element name. This would make the filter development easier and less error-prone, but then all filters would depend on this common header parsing function. This would not be a problem unless a filter had to access the HTTP request stream before any header parsing occurred (maybe because you wanted to manipulate or rearrange some header information).
If you want to provide additional base functionality, but still allow filters to be plugged into the request stream, you must define multiple filter chains. Each chain is then executed before or after the framework completes a processing step. For example, you can have a filter chain that is executed before any header parsing occurs and have a second filter chain that is executed after the headers are parsed (see Figure 6). If you take this concept to its logical conclusion, you can define a whole series of events. You can let the filter decide which event it wants to attach to, based on what function it performs and what services it needs from the framework.
This model shares some similarities to the event model described in the Observer pattern. In both cases, objects can "subscribe" to events without the original object being dependent on the observers. The object has no dependencies on any specific observers because it calls the observers through an abstract interface. The key difference between Intercepting Filter and Observer lies in the fact that the observer generally does not modify the source object; it "observes" passively what is going on in the source object. The purpose of Intercepting Filter, on the other hand, is to intercept and modify the context in which it is called.
Figure 6 also illustrates very well how each filter intercepts the sequence of events inside the Web server framework, hence the name Intercepting Filter.
Variations In most cases, filters are passive in the sense that they manipulate the context, but do not affect flow of execution. In the case of a filter intercepting a Web request, however, you often must design filters so that they redirect the request to a different page. For example, an authentication filter may redirect the request to an error page or to the logon page if the authentication fails. To illustrate how these filters affect the flow of the Web request, Figure 7 shows the sequence of a typical filter scenario, in which the intercepting filterdoes not intervene in the message flow.
Figure 8 shows an alternate sequence in which Filter One redirects the flow to a different page based on the type of request.
In this scenario, no page is rendered, but a redirect header (HTTP response 302) is produced and is returned to the client. This header causes the client to issue a new request to the URL specified in the redirect header. Because this type of redirection requires a second request from the client browser, it is often referred to as client-side redirect. The main disadvantage is that the client browser has to issue two requests to retrieve the page. This slows down the page display and can also lead to complications with bookmarking, because the client will bookmark the redirected URL, which is generally not good.
Server-side redirects, on the other hand, forward the request to a different page without requiring a roundtrip to the client. They accomplish this by returning control to the httpRunTime object, which calls a different Page Controller directly, passing along the request context. The transfer happens internally in the server without any involvement of the client. As a result, you do not have to repeat any common preprocessing of the request.
Server-side redirects are used in two common scenarios: URL manipulation can be used in Intercepting Filter to allow clients to use virtual URLs to pass parameters to the application. For example, a filter can convert http://example.com/clientabc into the URL http://www.example.com/start.aspx?Client=clientabc. This manipulation provides a level of indirection that lets the client bookmark a virtual URL that is not affected by internal changes to the application (for example, the migration from .asp to .aspx files). The other common technique that uses server-side redirection is the use of a Front Controller. The Front Controller processes all page requests in a central component and then passes control to the appropriate command. Front Controllers are useful for Web applications with dynamically configurable navigation paths.
Example Because intercepting filtersare such a common need when processing Web requests, most Web frameworks provide mechanisms for the application developer to hook intercepting filters into the request-response process. The Microsoft Windows platform provides two distinct mechanisms: Resulting Context The Intercepting Filter pattern results in the following benefits and liabilities: Benefits
Liabilities
Intercepting Filter vs. Controller
Because the intercepting filters are executed right before and after the controller, sometimes it may be difficult to determine whether to implement functionality inside the intercepting filteror inside the controller. The following criteria provide some guidelines when making this decision:
Related Patterns For more information, refer to the following related patterns: Acknowledgments [Alur01] Alur, Crupi, and Malks. Core J2EE Patterns: Best Practices and Design Strategies. Prentice-Hall, 2001. [Fowler03] Fowler, Martin. Patterns of Enterprise Application Architecture. Addison-Wesley, 2003. [Gamma95] Gamma, Helm, Johnson, and Vlissides. Design Patterns: Elements of Reusable Object-Oriented Software. Addison-Wesley, 1995. [Buschmann96] Buschmann, Frank, et al. Pattern-Oriented Software Architecture, Vol 1. Wiley & Sons, 1996. [Schmidt00] Schmidt, et al. Pattern-Oriented Software Architecture, Vol 2. Wiley & Sons, 2000. |