Modifying the HTTP Response Using FiltersBy Scott Mitchell
When a browser requests an ASP.NET page from a web server, the ASP.NET engine takes that request through a number of steps that, together, generate the resulting markup, which is returned to the requesting browser for display. The stages in this process are referred to as the HTTP Pipeline and perform tasks like authentication, authorization, and having the requested page render its content. During one of the later stages in the HTTP Pipeline the rendered markup is handed off to a response filter which, if supplied, has an opportunity to inspect and modify the markup before it is returned to the requesting browser.
With a little bit of code you can create your own response filters and associate them with a particular page, a particular type of page (such as ASP.NET resources that generate HTML), or for all ASP.NET resources. And if you are using IIS 7's integrated mode you can have your filter work with the output of any content type. This article provides an overview of response filters and shows two such filters: a naive filter that strips out whitespace to reduce the size of the markup sent over the wire, and a filter that adds a copyright message to the bottom of all web pages. You can download these two filters, along with a simple demo application, at the end of this article, with examples in both C# and Visual Basic.
Read on to learn more!
A Quick Primer on Response Filters
Response filters were introduced in ASP.NET version 1.1 and provide developers a means to programmatically inspect and modify the content of a requested resource after said has been generated but before it has been sent back to the client. When an ASP.NET page is requested it is rendered and its output is written to an
HttpWriterobject takes the content written to it and, periodically, writes the content out to a stream, which is the data stream returned to the client. For performance purposes, the
HttpWriterobject buffers the output it sends to the stream in chunks (rather than sending each character, one at a time, to the stream).
A stream is an object that extends the
System.IO namespace. In a nutshell, a stream functions as
a buffer where data can be written to and read from. There are a number of built in streams in the .NET Framework that use various backing stores for
their buffer. For instance, the
MemoryStream class is
a stream that holds its buffer in memory; a
on the other hand, implements its buffer as a file on disk.
Streams have an input and an output, an place from which to add data and a place from which to extract data. Data is added to a stream via a call to its
Write method and extracted via a call to its
Read method. The following diagram shows the stream used by the
object - the
HttpWriter object writes data from the rendered page into the stream, which is then read and sent down to the client.
One nice feature of streams is that they can be chained. It is possible to hook up two (or more) streams so that the output of one feeds into
the input of another. Response filters provide developers a way to chain custom stream with the stream used by the
as the following diagram shows.
Building a Simple Whitespace Stripping Response Filter
The download available at the end of this article includes two response filter examples. The first one I'd like to explore is the
|The Following Whitespace Stripping Filter Should NOT Be Used in Production|
The following filter was created to illustrate how response filters work. It should not be used in a production environment because it overaggressively
strips out whitespace. For example, the code removes all carriage returns from the page's rendered output. This can cause problems if you have
The above script will get compressed to the following:
As you can see, the comment and
As noted earlier in this article, response filters are streams. As a result, when building a response filter you must create a class
that extends the
Stream class. The
Stream class is an abstract class that defines the base functionality for all streams. If you
have your response filter inherit from this class directly you will have to implement a number of methods yourself. A better choice is to extend the
MemoryStream class, which alreay defines these boilerplate methods. Here is the shell of our response stream,
(Download the code from the bottom of this article to see the C# version of this code.)
As illustrated by the diagram above, a chained stream is one that receives input and then outputs it into another stream. Because a response filter serves
as a chained stream we need to tell the response filter what stream to output its data to. This is handled by creating a private member variable of
outputStream) and then creating a constructor that accepts a
Stream as input.
The last step is to override the base class's
Write method. The
Write method is passed a buffer of bytes ready to be sent to the
output stream. From this method we can inspect the buffer and perform any modifications to it before sending it along to the output stream. Keep in mind
Write method may be called multiple times for a single page. Recall that the
HttpWriter object chunks together data
and then writes it to its output stream. Each time the
HttpWriter sends out a chunk of data your response filter's
method is invoked.
In the case of a filter that strips whitespace we need the overridden
Write method to first convert the passed-in byte buffer into a
string, strip out any unecessary whitespace, and then send that stripped output to the output stream. I accomplished this with a couple of regular
expressions that remove carriage returns, tabs, and multiple, consecutive spaces.
Applying a Response Filter
Responseobject has a
Filterproperty that is used to chain together response filters. There are three ways you can set this property:
- From a particular web page. From within a web page you can set the
Response.Filterproperty. This affects the specific page that has this code. Add the following code to a page's
Page_Loadevent handler to apply a response filter:
Response.Filter = new ResponseFilterClassName(Response.Filter)
In the case of the
WhitespaceFilterVBresponse filter, the code would look like:
Response.Filter = new WhitespaceFilterVB(Response.Filter)
- From an appropriate event handler in
Global.asax. Response filters are applied after the
PostReleaseRequestStateevent. Therefore, you can add a response filter during this event or earlier. For instance, the following event handler in
WhitespaceFilterVBfilter to all pages with a
Sub Application_PostReleaseRequestState(ByVal sender As Object, ByVal e As EventArgs)
If Response.ContentType = "text/html" Then
Response.Filter = New WhitespaceFilterVB(Response.Filter)
- From an HTTP Module. HTTP Modules are managed classes that can respond to application
events (such as
PostReleaseRequestState). An HTTP Module could be used to assign the
WhitespaceFilterVBresponse filter in action. The demo's homepage (
Default.aspx) contains two TextBox controls - the first one shows the raw file contents of the
WhitespaceFilterDemo.aspxpage, which includes carriage returns, tabs, and miscellaneous spaces and clocks in at 2,603 bytes. The second TextBox control on the page shows the content of the
WhitespaceFilterDemo.aspxpage when viewed through a browser. The extraneous whitespace has been stripped, reducing the page's rendered content down to 2,282 bytes. The screen shot below shows the
Default.aspxpage in action.
Chainging Together Multiple Response Filters
As we just saw, it's possible to chain a custom response filter between the
HttpWriterobject and the output stream, but there's nothing that limits you to chaining in just one stream. This article's download includes a second response filter named
AddCopyrightFilterthat injects a copyright message at the bottom of the pages that the filter has been applied to. Specifically, it adds the following content immediately before the closing body tag (
AddCopyrightFilter response filter extends the
MemoryStream class and overrides its
Write method, but works a little differently than the
Instead of processing each chunk of data that comes into the
AddCopyrightFilter buffers the incoming data into
StringBuilder. Once the buffer contains the content "</html>", it injects the copyright notice and sends the entire buffered
text to the next stream in the chain. (This behavior presupposes that the pages it works with end with the text "</html>". If this response filter
is used on a page without an ending "</html>" no data will be sent down to the client!)
WhitespaceFilter response filters can be chained together. For example, you can set the
property like so:
The demo available for download does not use the exact code above, but instead implements the chained response filters by hooking up
WhitespaceFilter in the
Response filters are a mechanism by which developers can programmatically inspect and modify the outgoing data stream after a web page's content has been rendered but before the data has been returned to the client. Response filters can be used to compress data or modify the outgoing content, as in adding a copyright notice or other boilerplate text. This article showed two response filter examples: one that (naively) stripped whitespace from the rendered HTML and another that added a copyright message to the bottom of all pages the filter applies to.