Пользовательский модуль IIS конфликтует с gzip - PullRequest
2 голосов
/ 10 апреля 2011

В качестве эксперимента я занимался идеей создания управляемого модуля 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 не может напрямую изменять поток ответов (я думаю), и что правильный способ - добавить поток выходного фильтра, который выполняет эту работу. Я написал следующий тестовый модуль:

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 сохраняет версию gzipped и в любом запросе подпоследовательности мой пользовательский поток передается потоку gzipped. Я не нашел способа обнаружить его, но, возможно, есть более глубокая проблема в том, что я не совсем понимаю, как должны работать модули IIS. Кажется неправильным, что мой модуль должен делать какие-либо предположения о другом модуле, или, возможно, по крайней мере, можно определить порядок, в котором они обрабатывают запрос в конфигурации IIS. Однако это, похоже, не имеет смысла, поскольку каждый модуль может зарегистрироваться для обработки любых событий в жизненном цикле запроса.

Так что, если бы мне пришлось переформулировать свои мысли на более изощренные вопросы, вопросы были бы:

Как я могу убедиться в том, что мой модуль постобработки / модуля вызывается после того, как файл прочитан и отправлен сервером модулем StaticFileModule, но до StaticCompressionModule? И имеет ли этот вопрос смысл? Исходя из наблюдений выше, это кажется немного противоречивым.

1 Ответ

2 голосов
/ 10 апреля 2011

Существует конфигурация для статического сжатия файлов, включающая определенные типы файлов или mime, я забыл, какой именно. Если вы хотите использовать статическое сжатие, оно должно быть только для файлов, которые на самом деле являются статическими. Тот факт, что вы переписываете их с помощью HttpModule, означает, что они больше не статичны. Таким образом, вы можете использовать динамическое сжатие для этих типов файлов, и, как и следовало ожидать, вы захотите убедиться, что динамическое сжатие будет включать соответствующие типы MIME или расширения файлов.

...