iis 7 - Módulo de IIS personalizado en conflicto con gzip
iis-7 httpmodule (1)
Como experimento, he estado jugando con la idea de crear un módulo gestionado por IIS para modificar archivos CSS en marcha. La historia de fondo es que todas las aplicaciones web mantienen un número de versión compartido y común que adjuntamos a cada JS, CSS y referencia de imagen (en HTML) y quería modificar el contenido real del CSS para agregar también el número de versión a las referencias de imagen en CSS. Entonces, por ejemplo:
span.warning { background-image: url(warning-icon.png) }
debe convertirse:
span.warning { background-image: url(warning-icon.png?123) }
Ahora, sé que hay muchos enfoques diferentes para este problema (y algunos quizás sean mejores), pero me preguntaba si alguien puede responder mi pregunta relacionada con la que he estado jugando.
Hasta ahora he aprendido que un módulo HTTP administrado no puede modificar directamente la secuencia de respuesta (creo) y que una forma correcta es agregar un filtro de salida que hace el trabajo. He escrito el siguiente módulo de prueba:
public class HttpCSSModule : IHttpModule
{
public void Init(HttpApplication httpApplication)
{
httpApplication.BeginRequest += new EventHandler(
(s, e) => AttachFilter((HttpApplication)s));
}
private void AttachFilter(HttpApplication httpApplication)
{
HttpRequest httpRequest = httpApplication.Context.Request;
HttpResponse httpResponse = httpApplication.Context.Response;
if (httpRequest.Path.EndsWith(".css", StringComparison.CurrentCultureIgnoreCase))
{
if (!string.IsNullOrEmpty(httpRequest.Url.Query))
{
httpResponse.Filter = new CSSResponseStreamFilter(
httpResponse.Filter, httpRequest.Url.Query);
}
}
}
public void Dispose()
{
}
private class CSSResponseStreamFilter : Stream
{
private Stream inner;
private string version;
private MemoryStream responseBuffer = new MemoryStream();
public CSSResponseStreamFilter(Stream inner, string version)
{
this.inner = inner;
this.version = version;
}
public override void Close()
{
if (responseBuffer.Length != 0)
{
string stylesheet = Encoding.ASCII.GetString(
responseBuffer.GetBuffer(), 0, (int)responseBuffer.Length);
// crude, just testing
string versionedStylesheet = stylesheet.
Replace(".png", ".png" + version).
Replace(".jpg", ".jpg" + version).
Replace(".gif", ".gif" + version);
byte[] outputBytes = Encoding.ASCII.GetBytes(versionedStylesheet);
innerStream.Write(outputBytes, 0, outputBytes.Length);
}
innerStream.Close();
}
public override void Write(byte[] buffer, int offset, int count)
{
responseBuffer.Write(buffer, offset, count);
}
// other Stream members
}
}
El módulo funciona, pero no siempre, y hay cosas que no entiendo. El mayor problema es que el módulo no funciona cuando la compresión de archivos estáticos está activada. Cuando la compresión de archivos estáticos está activada, la primera solicitud a un archivo CSS sirve al archivo como habitualmente, pero presumiblemente IIS mantiene la versión comprimida y en cualquier solicitud de subsecuencia mi secuencia personalizada pasa la corriente gzip. No he encontrado una forma de detectarlo, pero probablemente haya un problema más profundo en el sentido de que realmente no entiendo cómo se supone que los módulos IIS funcionan. Parece incorrecto que mi módulo deba suponer en otro módulo, o tal vez al menos, uno debería ser capaz de definir el orden en que manejan la solicitud en la configuración de IIS. Sin embargo, esto no parece tener sentido porque cada módulo se puede registrar para manejar cualquier evento en el ciclo de vida de la solicitud.
Entonces, si tuviera que reformular mis pensamientos a preguntas más refinadas, las preguntas serían:
¿Cómo puedo asegurarme de que se llame a mi módulo posterior de procesamiento / módulo una vez que el módulo StaticFileModule haya leído y servido el servidor pero antes de StaticCompressionModule? ¿Y la pregunta tiene sentido? En base a las observaciones anteriores, parece un poco contradictorio.
Hay una configuración para la compresión de archivos estáticos para incluir archivos particulares o tipos de mime, se me olvida cuál. Si desea usar compresión estática, solo debe estar en archivos que realmente sean estáticos. El hecho de que los estarías reescribiendo con un HttpModule significa que ya no son estáticos. Por lo tanto, podría usar la compresión dinámica para esos tipos de archivos, y como era de esperar, querrá asegurarse de que su compresión dinámica incluya los tipos de mime o extensiones de archivos apropiados.