Очищаем HTML от лишних знаков

Однажды посмотрев HTML-документ, генерируемый ASP.NET и содержащий GridView, я заметил, что большое количество символов, содержащихся на странице - пробелы и знаки табуляции. Очевидно, ASP.NET щедро расставлял их, где только можно.

Конечно, избавиться от лишних килобайтов HTML-документа можно, воспользовавшись библиотеками, которые сжимают HTML-документы при помощи различных алгоритмов. Но такой метод заставит сервер сначала сжать генерируемый HTML-документ, а браузеру пользователя придется распаковывать пришедший ему документ.

Я же хочу в своей статье предложить способ как избавиться от различных ненужных символов, которые содержатся на страницах. Этот способ основан на использовании сборки HTTPModule.

Так что же такое HTTPModule? Вот что о нем говорится в библиотеке MSDN:

HttpModule — это сборка, реализующая интерфейс IHttpModule и обрабатывающая события. ASP.NET включает набор сборок HttpModule, которые могут использоваться в приложениях пользователей. Например, SessionStateModule предоставляется ASP.NET для поставки в приложение служб состояния сеанса. Пользовательские обработчики HttpModule могут быть созданы в качестве ответа на событие ASP.NET или событие пользователя.

Общая процедура написания обработчика HttpModule следующая:

* Реализация интерфейса IHttpModule.
* Обработка метода Init и регистрация необходимых событий.
* Обработка событий.
* Реализация (при необходимости) метода Dispose, если требуется выполнить очистку.
* Регистрация модуля в файле Web.config.

HTTP Module включается в процесс обработки запроса пользователя после создания объекта HTTP Application и перед созданием HTTP Handle, так что HTTP Module позволяет обработать следующие события объекта HTTP Application:

* BeginRequest
* AuthenticateRequest
* PostAuthenticateRequest
* AuthorizeRequest
* PostAuthorizeRequest
* ResolveRequestCache
* PostResolveRequestCache
* PostMapRequestHandler
* AcquireRequestState
* PostAcquireRequestState
* PreRequestHandlerExecute
* PostRequestHandlerExecute
* ReleaseRequestState
* PostReleaseRequestState
* UpdateRequestCache
* PostUpdateRequestCache
* EndRequest

Подключение обработчиков событий выполняется в методе Init класса HTTPModule.

В своем примере мне необходимо подключить обработчик на 2 события ReleaseRequestState, как раз после того, как была сгенерирована HTML-версия страницы и объект Response готов к отправке пользователю.

/// <summary> 
        /// Подключение обработчиков событий 
        /// </summary> 
        public void Init(HttpApplication context) 
        { 
             //Подключаем обработчик на событие ReleaseRequestState 
             context.ReleaseRequestState += new EventHandler(this.context_Clear); 
             //Подключаем обработчик на событие PreSendRequestHeaders 
             context.PreSendRequestHeaders += new EventHandler(this.context_Clear); 
             //Два обработчика необходимы для совместимости с библиотеками сжатия HTML-документов 
        }

Сам обработчик будет иметь следующий вид:

/// <summary> 
        /// Обработчик события PostRequestHandlerExecute 
        /// </summary> 
        void context_Clear(object sender, EventArgs e) 
        { 
             HttpApplication app = (HttpApplication)sender; //Получение HTTP Application 
             //Получаем имя файла который обрабатывается 
             string realPath = app.Request.Path.Remove(0, app.Request.ApplicationPath.Length + 1);  
             //Проверяем не является ли он ссылкой на ресурс сборки             
             if (realPath == "WebResource.axd")  
                  return; 
             //Проверяем тип содержимого 
             if (app.Response.ContentType == "text/html" || app.Response.ContentType == "text/javascript")           
                 //Устанавливаем фильтр-обработчик 
                 app.Context.Response.Filter = new HTMLClearer(app.Context.Response.Filter);  
        }

Фильтр-обработчик - это самое главное. Он позволяет изменять содержимое объекта Response. А дополнительные проверки необходимы, чтобы исключить обработку ресурсов сборок и документов, тип которых отличен от text/html и text/javascript (в документах другого типа нет необходимости убирать лишние символы).

Теперь уделим внимание обработчику содержимого Response.

Это класс, являющийся наследником System.IO.Stream. В его реализации нам интересен только один метод - это метод Write:

/// <summary> 
        /// Обрабатываем данные поступающие в Response 
        /// </summary> 
        public override void Write(byte[] buffer, int offset, int count) 
            { 
                //Преобразовываем массив байт в строку 
                string s = System.Text.Encoding.UTF8.GetString(buffer); 
                //Используя регулярные выражения убираем все ненужные символы 
                s = Regex.Replace(s, ">(rn){0,10} {0,20}t{0,10}(rn){0,10}t{0,10}(rn){0,10} {0,20}(rn){0,10} {0,20}<", "><", RegexOptions.Compiled); 
                s = Regex.Replace(s, ";(rn){0,10} {0,20}t{0,10}(rn){0,10}t{0,10}", ";", RegexOptions.Compiled); 
                s = Regex.Replace(s, "{(rn){0,10} {0,20}t{0,10}(rn){0,10}t{0,10}", "{", RegexOptions.Compiled); 
                s = Regex.Replace(s, ">(rn){0,10}t{0,10}<", "><", RegexOptions.Compiled); 
                s = Regex.Replace(s, ">r{0,10}t{0,10}<", "><", RegexOptions.Compiled); 
                //Получивщуюся строку преобразовываем обратно в byte 
                byte[] outdata = System.Text.Encoding.UTF8.GetBytes(s); 
                //Записываем ее в Response 
                _HTML.Write(outdata, 0, outdata.Length); 
            }

А также конструктор класса:

public HTMLClearer(System.IO.Stream HTML) 
                { _HTML = HTML; }

Для демонстрации примера использования HTTP Module и обработчика содержимого объекта HTTP Response создадим проект Class Library и назовем его - HTMLClearer. В этом проекте следует создать файл HTMLClearer.cs, содержащей следующий текст:

using System; 
        using System.Collections.Generic; 
        using System.Text; 
        using System.Web; 
        using System.Text.RegularExpressions; 
        namespace HTMLClearer 
        { 
            public class HTMLClearer : System.IO.Stream 
            { 
                System.IO.Stream _HTML; 
                public HTMLClearer(System.IO.Stream HTML) 
                { _HTML = HTML; } 
                #region Стандартные методы и свойства 
                public override bool CanRead 
                { get { return false; } } 
                public override bool CanSeek 
                { get { return false; } } 
                public override bool CanWrite 
                { get { return true; } } 
                public override long Length 
                { get { return _HTML.Length; } } 
                public override long Position 
                { 
                    get { return _HTML.Position ; } 
                    set { _HTML.Position  = value; } 
                } 
                public override long Seek(long offset, System.IO.SeekOrigin origin) 
                { return _HTML.Seek(offset, origin); } 
                public override void SetLength(long value) 
                { _HTML.SetLength(value); } 
                public override void Flush() 
                { _HTML.Flush(); } 
                public override int Read(byte[] buffer, int offset, int count) 
                { return _HTML.Read(buffer, offset, count); } 
                #endregion 
                /// <summary> 
                /// Обрабатываем данные поступающие в Response 
                /// </summary> 
                public override void Write(byte[] buffer, int offset, int count) 
                { 
                    //Преобразовываем массив байт в строку 
                    string s = System.Text.Encoding.UTF8.GetString(buffer); 
                    //Используя регулярные выражения убираем все ненужные символы 
                    s = Regex.Replace(s, ">(rn){0,10} {0,20}t{0,10}(rn){0,10}t{0,10}(rn){0,10} {0,20}(rn){0,10} {0,20}<", "><", RegexOptions.Compiled); 
                    s = Regex.Replace(s, ";(rn){0,10} {0,20}t{0,10}(rn){0,10}t{0,10}", ";", RegexOptions.Compiled); 
                    s = Regex.Replace(s, "{(rn){0,10} {0,20}t{0,10}(rn){0,10}t{0,10}", "{", RegexOptions.Compiled); 
                    s = Regex.Replace(s, ">(rn){0,10}t{0,10}<", "><", RegexOptions.Compiled); 
                    s = Regex.Replace(s, ">r{0,10}t{0,10}<", "><", RegexOptions.Compiled); 
                    //Получивщуюся строку преобразовываем обратно в byte 
                    byte[] outdata = System.Text.Encoding.UTF8.GetBytes(s); 
                    //Записываем ее в Response 
                    _HTML.Write(outdata, 0, outdata.Length); 
                } 
            } 
            public class HTTPModule_Clearer : IHttpModule 
            { 
                #region IHttpModule Members 
                public void Dispose() 
                {           
                } 
                /// <summary> 
                /// Подключение обработчиков событий 
                /// </summary> 
                public void Init(HttpApplication context) 
                { 
                   //Подключаем обработчик на событие ReleaseRequestState 
                   context.ReleaseRequestState += new EventHandler(this.context_Clear); 
                   //Подключаем обработчик на событие PreSendRequestHeaders 
                   context.PreSendRequestHeaders += new EventHandler(this.context_Clear); 
                   //Два обработчика необходимы для совместимости с библиотеками сжатия HTML-документов 
                } 
                /// <summary> 
                /// Обработчик события PostRequestHandlerExecute 
                /// </summary> 
                void context_Clear(object sender, EventArgs e) 
                { 
                    HttpApplication app = (HttpApplication)sender; //Получение HTTP Application 
                    string realPath = app.Request.Path.Remove(0, app.Request.ApplicationPath.Length + 1); //Получаем имя файла который обрабатывается 
                    if (realPath == "WebResource.axd") //Проверяем не является ли он ссылкой на ресурс сборки 
                        return; 
                    if (app.Response.ContentType == "text/html" || app.Response.ContentType == "text/javascript") //Проверяем тип содержимого 
                        app.Context.Response.Filter = new HTMLClearer(app.Context.Response.Filter); //Устанавливаем фильтр обработчик 
                } 
                #endregion 
            } 
        }

После всех этих манипуляций компилируем проект, и получившуюся библиотеку через Add Reference подключаем к Веб-сайту.

Теперь нам необходимо подключить HTTP Module к общему потоку обработки запросов. Для этого в файле web.config необходимо сделать некоторые изменения, а именно в раздел system.web добавить ссылку на модуль:

<httpModules>
              <add name="HTTPModule_Clearer"  type="HTMLClearer.HTTPModule_Clearer, HTMLClearer"/> 
         </httpModules>

Общий вид тега <httpModules> взятый из MSDN выглядит так:

<httpModules> 
       <add type="classname,assemblyname" name="modulename"/> 
       <remove name="modulename"/> 
       <clear/> 
    </httpModules>

<add>
Добавляет в приложение класс HttpModule.

Обратите внимание, что в случае сочетания команда-путь, идентичного уже указанному ранее (например, в файле Web.config родительской папки), второй вызов <add> переопределяет предыдущее значение.

<remove>
Удаляет из приложения класс HttpModule.

<clear>
Удаляет из приложения все сопоставления HttpModule.

При использовании данного модуля размер HTML-страничек, отправляемых пользователю, уменьшается примерно на 10%, что не может не сказаться на трафике, как пользователя, так и сервера.

P.S.

Использование HTTP Module позволяет также вносить изменения в отправляемый пользователю ответ (например, если есть необходимость добавить к странице Header или Footer).

Также, дописав метод Write, можно сохранять определенные части страницы или страницы целиком в базу данных или в файл.

23 ноября 2006 в 13:00
Материалы по теме
{"url":"http://www.fastvps.ru/", "src":"/images/advbanners/fastvps.png", "alt":"Хостинг Fastvps.ru. Наш выбор!"}
Заработок