Rewrite asp.net response before it gets cached with dynamicCompressionBeforeCache
My ASP.NET library installs a response filter to parse and alter the HTML output of pages. This is working until the following is enabled in web.config:
<system.webServer>
<urlCompression doDynamicCompression="true" dynamicCompressi开发者_开发知识库onBeforeCache="true" />
</system.webServer>
My response filter is still called, but is passed what seems to be compressed data, not the original HTML.
How can I ensure my rewriting occurs before the page is stored in the output cache?
Setting dynamicCompressionBeforeCache to false is not an option, since this library is designed to be used in other people's web applications who may have good reason to enable compression.
alexb's answer helped me figure out a solution.
The key was realising that my response filter was getting run after the IIS DynamicCompressionModule. This can be fixed by forcing the filter to run by flushing the response immediately after the request handler executes.
public class MyModule : IHttpModule
{
public void Init(HttpApplication application)
{
application.BeginRequest += application_BeginRequest;
application.PostRequestHandlerExecute += application_PostRequestHandlerExecute;
}
void application_BeginRequest(object sender, EventArgs e)
{
// Install response filter
var response = ((HttpApplication)sender).Context.Response;
response.Filter = new RewritingFilter(response.Filter);
}
void application_PostRequestHandlerExecute(object sender, EventArgs e)
{
// Flush immediately after the request handler has finished.
// This is Before the output cache compression happens.
var response = ((HttpApplication)sender).Context.Response;
response.Flush();
}
public void Dispose()
{
}
}
According to this blog post, the DynamicCompressionModule, which is responsible for handling the dynamic compression, runs during the ReleaseRequestState stage (which occurs before the UpdateRequestCache stage, when the page is being stored to the output cache). Therefore:
- if you want to process the response before dynamic compression is performed, then you should place your filtering code in a HttpModule that hooks up to the PostRequestHandlerExecute event;
- if you want to process the response caching is performed, but after compression, then you should place your filtering code in a HttpModule that hooks up the PostReleaseRequestState event.
Here's an example on how to do this:
public class SampleModule : IHttpModule
{
public void Dispose()
{
return;
}
public void Init(HttpApplication context)
{
context.PostRequestHandlerExecute += new EventHandler(App_OnPostRequestHandlerExecute);
context.PostReleaseRequestState += new EventHandler(App_OnPostReleaseRequestState);
}
private void App_OnPostRequestHandlerExecute(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpResponse response = app.Context.Response;
response.Write("<b>Modified!</b>");
}
private void App_OnPostReleaseRequestState(object sender, EventArgs e)
{
HttpApplication app = (HttpApplication)sender;
HttpResponse response = app.Context.Response;
//check if the output has been compressed - make sure to actually test if for whatever content encoding you want to deal with
if (response.Headers["Content-Encoding"] != null)
{
//do stuff
}
else
{
//do some other stuff
}
}
}
And then register your module using the web.config file:
<add name="SampleModule" type="My.ModuleNamespace.SampleModule" />
精彩评论