Implementing an IIS Application Filter Using .NET HttpModules and Response Filtering

Implementing an IIS Application Filter Using .NET HttpModules and Response Filtering

n this article, I will concentrate on the critical differences in the processing model implemented by HttpHandlers versus that implemented by HttpModules. I’ll be presenting specific coding samples as necessary in order in order to illustrate various points, but I won’t be presenting a comprehensive sample of my own implementation.  A wealth of information and code samples are available to demonstrate how to establish an HttpModule to participate in your application’s web requests and I’ll present these links toward the end of the article. In the meantime, It’s my hope that this article will complement the existing samples and documentation and assist you with the development of your own IIS Application filtering solution.

If you’ve worked with ISAPI filters under IIS4, the HttpModule processing model is probably nothing new to you. However ISAPI filtering under IIS4 was relatively out-of-bounds for VB developers and therefore the fundamental processing model may be less than intuitive to many VB developers.

HttpHandler?the Target for your Request
The HttpHandler model is relatively easy to understand. An HttpHandler is the target of a web request. In fact, a WebForm (.aspx) page derives from System.Web.UI.Page which implements the HttpHandler interface. Check it out in the object browser.

So as far as the processing model is concerned, writing your own HttpHandler is no different than writing a WebForm page or any other .aspx (or .asp) script. As with these scripts, the context of each web request is made available to the HttpHandler on a request-by-request basis. This context will include the global Server and Application objects as well as the Request, Response and Session objects which are relevant to the current request.

An HttpHandler is created specifically (or retrieved from the pool) for each request and is dedicated exclusively to that request for the remainder of its lifetime (or until it is released back to the pool). The active lifetime for each particular request begins when control enters ProcessRequest, and extends until control exits ProcessRequest. During this time, request specific data may be freely accumulated and cached globally since the entire scope of the active lifetime is dedicated to a specific request.

Note the IsReusable property defined in the IHttpHandler interface. If this is returned as true, then the HttpHandler will not be destroyed when control exits ProcessRequest. Rather, it will be released to the pool for use by a future requestor. Naturally, this means that request specific data must be de-initialized at the end of a request, or reinitialized at the beginning of a request. (Cautious developers may elect to initialize at both points.) This consideration is standard to object pooling in general and is not specific to HttpHandlers.

In my previous article, I described how I used an HttpHandler in order to implement a centralized request controller for my .NET web application. See that article for more details, but the basic strategy is to map all requests to a particular HttpHandler. That HttpHandler uses information coming in from the browser (e.g. QueryString, Cookie) in order to create and call the relevant handler class. The benefit of this model is that common pre-processing and post-processing can be centralized to the HttpHandler application controller. This guarantees that the relevant pre-processing and post-processing must be called for every request. It also means that when either of these request stages need to be modified, the change can be effected to a central location which will immediately impact all requests across the entire application.

The HttpHandler approach is a robust model for implementing a web application where request handlers are implemented as compiled classes which implement a common interface. As mentioned earlier, the HttpHandler instantiates the correct class for the current request and calls the defined interface method. The HttpHandler approach is less helpful though, if we’d like to take advantage of the RAD features offered by Visual Studio .NET and WebForms for rapid web application development.

As HttpHandlers, WebForms are designed to be invoked directly as the target of a browser request. However, using the centralized HttpHandler as an application controller means that all requests are mapped to that particular WebHandler. At this point, the application controller WebHandler (and not the WebForm script) becomes the target of the HTTP request. This becomes a significant barrier to WebForms style development.

I recently worked on a project where the rules changed midway through development. Initially, the developers who had come from a VB6/ASP background were quite amenable to the prospects of implementing the application request handlers as compiled classes. However, after seeing the brand new capabilities of .NET WebForms they decided that they just could not commit to a long-term restriction against the use of WebForms to develop application request handlers. Since WebForm facilities are based on the fundamentals of HTTP (query parameters, cookies and form fields) it is certainly possible, using the System.NET classes, to write code to issue a secondary request to, and to receive the response from, the .NET WebForm for those requests which are implemented as WebForms. Ultimately though, this would be a lot of work and tantamount to rewriting the web server! Fortunately, there is a counterpart mechanism, the HttpModule which allows us to impose centralized application-wide request processing without interfering with the target of the request emanating from the browser.

Before moving on to the HttpModule, I’d like to just re-emphasize the limitation presented by HttpHandler. Since the HttpHandler becomes the target of the request, it is difficult to engineer the application controller as an HttpHandler in an environment where the application request handlers are themselves implemented as HttpHandlers (as is the case with WebForms). This is not a flaw in the HttpHandler implementation; On the contrary, the HttpHandler is specifically designed to fulfill  the role of a request handler, rather than an application controller. In order to implement an application controller which can filter every application request, without disturbing the target HttpHandler for that request, we need to take a look at the HttpModule.

HttpModule?the Filter for all Requests
The HttpModule does not replace the target of a request, however the HttpModule receives notification at various processing points during the lifespan of a request. Since (as is the case with HttpHandlers) we can map an HttpModule to all application request pages we can use an HttpModule as the foundation for our web application controller. Let’s start off by taking a look at the IHttpModule interface.

The first thing to notice about this interface is the lack of an IsPoolable property. There is a very good reason for this. Unlike HttpHandlers, which are instantiated for each concurrent request, only one instance of any given HttpModule is instantiated for any given application. Naturally, this makes the issue of pooling irrelevant.

Here’s a snippet from the Web.config file on the FPSNow! site:

HttpModule?the Filter for all Requests (cont'd)
One important point still needs to be clarified. In the preceding paragraph, where the workings of the WebFilter class are described, I made numerous references to the current request. Knowing as we do, that a single instance of the WebFilter filters every single request for the entire application, how, for any given event, do we identify the context of the current request? If WebFilter is assembling the page footer for User A, and an OnBeginRequest event is raised for a request coming in from User B, how do we keep from writing User B’s header into User A’s response? And vice versa?

As it turns out, this is a relatively trivial factor, which is greatly simplified by the .NET Framework environment. The fact is, at any given time, code inside of an HttpModule has access to the context of the request which is currently being operated on, with one simple statement:

C#HttpContext httpCtx = HttpContext.Current;VB.NETDim httpCtx As HttpContext = HttpContext.Current

That’s it?simple! Of course, we still need to keep in mind that a single class instance of WebFilter serves as the HttpModule to filter all application requests. Request specific data should never be stored in a class member variable because at any given time an event from a different request might be raised. If you need to preserve data from the beginning of the request (OnBeginRequest) to the end of the request (OnPostRequestHandlerExecute), the most logical place to put it would be in the Session object.

So let’s recap; I’ve installed an HttpModule to monitor all application requests. At the start of a request the page header is assembled inside of the HttpModule. Then control proceeds to the HttpHandler which returns the main page content section. After the HttpHandler is done, the footer is assembled inside the HttpModule. And then the entire page is written back to the browser. Not bad. But there’s still one critical feature which is missing.

Since the header and footer are pre-pended and appended to the response stream returned by the HttpHandler it is easy to accomplish this inside the HttpModule. But what if we actually want to modify the response stream which is returned by the HttpHandler? And here’s why I’d want to do that. I perform a particular XSL transformation in order to format the menu on each page. In order to free page developers from having to worry about this, I’d like to implement this in my application controller. All the page designer needs to do is insert the string {menu} somewhere into the HTML output and the menu will be rendered into the designated location. Similarly, in some of the pages, developers simply encode the string {tabs} in order to have a context sensitive tabstrip rendered in place. In order to do this though, we need some way of accessing the response stream from the HttpHandler. However, although the HttpModule is notified when the HttpHandler has completed, it does not get access to the response data output from the HttpHandler.

That’s the bad news. The good news is, that there is a facility which will enable us to do this. It’s called response filtering and we use the Response.Filter property in order to implement it.

Take a look at the Response object and you’ll see that it has a Filter property. As described in the object browser, the Filter gets or sets a wrapping filter object used to modify the HTTP entity body before transmission’. This is exactly what we need to do in order to make modifications to the HTML output of the HttpHandler. The Filter property is declared as type System.IO.Stream. In order to assign our own class to this filter property we need to define our class as inheriting from System.IO.Stream:

C#public class PageFilter : System.IO.StreamVB.NETPublic Class PageFilter : Inherits System.IO.Stream

We now have a Stream class, PageFilter, which can be assigned to the Response.Filter property. By attaching PageFilter to the Response.Filter property, PageFilter will be notified at critical times as data is written to the Response buffer. The most significant event of course is the Write operation. When this method is called, you’ll have the opportunity to modify data as it’s being written to the Response buffer. (I combine this with 'Response.Buffer = true' so that my PageFilter receives the complete response stream in a single method invocation.):

C#public override void Write(byte[] buffer, int offset, int count)VB.NET


About Our Editorial Process

At DevX, we’re dedicated to tech entrepreneurship. Our team closely follows industry shifts, new products, AI breakthroughs, technology trends, and funding announcements. Articles undergo thorough editing to ensure accuracy and clarity, reflecting DevX’s style and supporting entrepreneurs in the tech sphere.

See our full editorial policy.

About Our Journalist