#iis-7 #httpmodule
#iis-7 #httpmodule
Вопрос:
В качестве эксперимента я поиграл с идеей создания управляемого IIS модуля для изменения CSS-файлов «на лету». Предыстория заключается в том, что все веб-приложения сохраняют общий номер версии, который мы добавляем к каждой ссылке на JS, CSS и изображение (в HTML), и я хотел изменить фактическое содержимое CSS, чтобы также добавлять номер версии к ссылкам на изображения в CSS. Так, например:
span.warning { background-image: url(warning-icon.png) }
должно стать:
span.warning { background-image: url(warning-icon.png?123) }
Теперь я знаю, что существует много разных подходов к этой проблеме (и некоторые, возможно, лучше), но мне было интересно, может ли кто-нибудь ответить на мой вопрос, связанный с тем, с которым я играл.
До сих пор я узнал, что управляемый HTTP-модуль не может напрямую изменять поток ответов (я думаю) и что правильный способ — добавить выходной фильтр steam, который выполняет эту работу. Я написал следующий тестовый модуль:
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
}
}
Модуль работает, но не всегда, и есть вещи, которые я не понимаю. Самая большая проблема заключается в том, что модуль не работает при включенном статическом сжатии файлов. Когда включено статическое сжатие файла, первый запрос к CSS-файлам обслуживает файл как обычно, но, предположительно, IIS сохраняет архивированную версию, и при любом последующем запросе моему пользовательскому потоку передается архивированный поток. Я не нашел способа обнаружить это, но, вероятно, существует более глубокая проблема в том, что я, возможно, не совсем понимаю, как должны работать модули IIS. Кажется неправильным, что мой модуль должен делать какие-либо предположения о другом модуле, или, возможно, по крайней мере, нужно иметь возможность определять порядок, в котором они обрабатывают запрос в конфигурации IIS. Однако, похоже, это не имеет смысла, поскольку каждый модуль может зарегистрироваться для обработки любых событий в жизненном цикле запроса.
Итак, если бы мне пришлось переформулировать свои мысли в более утонченные вопросы, вопросы были бы:
Как я могу убедиться, что моя постобработка / модуль вызывается после того, как файл прочитан и отправлен на сервер модулем StaticFileModule, но до StaticCompressionModule? И имеет ли вообще смысл этот вопрос? Основываясь на приведенных выше наблюдениях, это кажется немного противоречивым.
Комментарии:
1. У меня такая же проблема, и я часами работаю над ее устранением. : (Не могли бы вы объяснить мне, как вы решили эту проблему, пожалуйста?
Ответ №1:
Существует конфигурация для статического сжатия файлов, включающая определенные типы файлов или mime, я забыл, какой именно. Если вы хотите использовать статическое сжатие, оно должно выполняться только для файлов, которые на самом деле статичны. Тот факт, что вы будете переписывать их с помощью HttpModule, означает, что они больше не являются статическими. Итак, вы могли бы использовать динамическое сжатие для этих типов файлов, и, как и следовало ожидать, вы захотите убедиться, что ваше динамическое сжатие будет включать соответствующие типы mime или расширения файлов.
Комментарии:
1. Вы правы. Это устраняет проблему, и на самом деле это более логично (это больше не статический файл). Это также показывает, что модуль динамического сжатия (compdyn.dll ) и модуль статического сжатия (compstat.dll ) отличаются не только списком типов, которые они обрабатывают. Они должны интегрироваться в конвейер IIS различными способами.