2015-09-22
zelder
2015-10-05
22/09
2015

Постобработка разметки контента

Предназначение

Модуль при начале обработки запроса вставляет фильтр, для записи в поток всего контента.
По завершении обработки всего запроса, этот поток с данными обрабатывается по необходимости.
Примерная цель:
после создания контента всей страницы, необходимо собрать со страницы все DOMObject с определенными атрибутами и запустить на основе этого скрипт.

В данном примере контент обрабатывается у всех с ContentType равным "text/html".
Все асинхронные запросы будут также участвовать, и это необходимо учесть.

Настройка модуля

в web.config прописать:

<system.web>
    <httpModules>
        <add name="PostInformerModule" type="Site.FrontEnd.Web.Modules.PostInformerModule"/>
    </httpModules>
</system.web>
<system.webServer>
    <modules runAllManagedModulesForAllRequests="true">
        <add name="PostInformerModule" type="Site.FrontEnd.Web.Modules.PostInformerModule"/>
    </modules>
</system.webServer>

Дополнительно о настройках модуля тут.
В частности, раздел system.web для IIS 6.0 and IIS 7.0 в Classic mode;
Раздел system.webServer для IIS 7.0 в Integrated mode.
Модуль

Файл модуля PostInformerModule.cs

public class PostInformerModule : IHttpModule
{
        public void Dispose()
        {
        }
        public void Init(HttpApplication context)
        {
            context.PreRequestHandlerExecute += context_PreRequestHandlerExecute;
            context.PreSendRequestHeaders += context_PreSendRequestContent;
        }
        void context_PreRequestHandlerExecute(object sender, EventArgs e)
        {
            var application = sender as HttpApplication;
            if (application == null) return;
            var context = application.Context;
            var response = context.Response;
            //+ только страницы
            var cnt = response.ContentType;
            if (!cnt.Equals("text/html", StringComparison.CurrentCultureIgnoreCase))
            {
                return;
            }
            //+ добавляем фильтр для записи ответа
            response.Filter = new ResponseSniffer(response.Filter);
        }
        void context_PreSendRequestContent(object sender, EventArgs e)
        {
            var application = sender as HttpApplication;
            if (application == null) return;
            var context = application.Context;
            var response = context.Response;
            //+ только страницы
            var cnt = response.ContentType;
            if (!cnt.Equals("text/html", StringComparison.CurrentCultureIgnoreCase))
            {
                return;
            }
            var filter = response.Filter as ResponseSniffer;
            if (filter != null)
            {
                var reader = new StreamReader(new MemoryStream(filter.RecordStream.GetBuffer()));
                var source = reader.ReadToEnd();
                if (!String.IsNullOrWhiteSpace(source))
                {
                    // TODO:! при необходимости распарсить содержимое
                    
                    //! вставка нового контента
                    var pos = 0;
                    string s = "<script>console.log('wuiiii');</script>";
                    byte[] bytes = System.Text.Encoding.UTF8.GetBytes(s);
                    response.OutputStream.Write(bytes, pos, bytes.Length);
                }
            }
 }

Файл ResponseSniffer.cs

public class ResponseSniffer : Stream
{
            private readonly Stream ResponseStream;
            public MemoryStream RecordStream { get; set; }
            #region Implements of Stream
            public override bool CanRead
            {
                get { return ResponseStream.CanRead; }
            }
            public override bool CanSeek
            {
                get { return ResponseStream.CanSeek; }
            }
            public override bool CanWrite
            {
                get { return ResponseStream.CanWrite; }
            }
            public override void Flush()
            {
                ResponseStream.Flush();
            }
            public override long Length
            {
                get { return ResponseStream.Length; }
            }
            public override long Position
            {
                get
                {
                    return ResponseStream.Position;
                }
                set
                {
                    ResponseStream.Position = value;
                }
            }
            public override int Read(byte[] buffer, int offset, int count)
            {
                return ResponseStream.Read(buffer, offset, count);
            }
            public override long Seek(long offset, SeekOrigin origin)
            {
                return Seek(offset, origin);
            }
            public override void SetLength(long value)
            {
                ResponseStream.SetLength(value);
            }
            public override void Write(byte[] buffer, int offset, int count)
            {
                RecordStream.Write(buffer, offset, count);
                ResponseStream.Write(buffer, offset, count);
            }
            #endregion
            public ResponseSniffer(Stream stream)
            {
                RecordStream = new MemoryStream();
                ResponseStream = stream;
            }
}