Захват HTML, созданного из ASP.NET

Как мне лучше всего захватить HTML (в моем случае для ведения журнала), отображаемый aspx-страницей?

Я не хочу, чтобы мне приходилось писать обратно на страницу с помощью Response.Write, поскольку это портит макет моего сайта.

Использование потока Response.OutputStream или Response.Output приводит к возникновению исключения ArgumentException ({System.ArgumentException: поток не читался.)


person lastas    schedule 22.12.2008    source источник
comment
Короче говоря, переопределите метод Render для страницы. Аналогичный вопрос и ответы здесь   -  person Peter T. LaComb Jr.    schedule 22.12.2008


Ответы (3)


Хороший вопрос, мне пришлось попробовать и посмотреть, смогу ли я создать HttpModule, чтобы делать то, что вы описываете.

Мне не повезло с попыткой прочитать из потока ответов, но использование ResponseFilter дало мне возможность захватить контент.

Следующий код, кажется, работает довольно хорошо, и я подумал, что, возможно, вы могли бы использовать этот код в качестве основы. Но помните, что это просто то, что я быстро собрал, он никак не тестировался. Так что не используйте его в любой производственной среде без надлежащей проверки / тестирования и т. Д. Не стесняйтесь комментировать это;)

public class ResponseLoggerModule : IHttpModule
{
    private class ResponseCaptureStream : Stream
    {
        private readonly Stream _streamToCapture;
        private readonly Encoding _responseEncoding;

        private string _streamContent;
        public string StreamContent
        {
            get { return _streamContent; }
            private set
            {
                _streamContent = value;
            }
        }

        public ResponseCaptureStream(Stream streamToCapture, Encoding responseEncoding)
        {
            _responseEncoding = responseEncoding;
            _streamToCapture = streamToCapture;

        }

        public override bool CanRead
        {
            get { return _streamToCapture.CanRead; }
        }

        public override bool CanSeek
        {
            get { return _streamToCapture.CanSeek; }
        }

        public override bool CanWrite
        {
            get { return _streamToCapture.CanWrite; }
        }

        public override void Flush()
        {
            _streamToCapture.Flush();
        }

        public override long Length
        {
            get { return _streamToCapture.Length; }
        }

        public override long Position
        {
            get
            {
                return _streamToCapture.Position;
            }
            set
            {
                _streamToCapture.Position = value;
            }
        }

        public override int Read(byte[] buffer, int offset, int count)
        {
            return _streamToCapture.Read(buffer, offset, count);
        }

        public override long Seek(long offset, SeekOrigin origin)
        {
            return _streamToCapture.Seek(offset, origin);
        }

        public override void SetLength(long value)
        {
            _streamToCapture.SetLength(value);
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            _streamContent += _responseEncoding.GetString(buffer);
            _streamToCapture.Write(buffer, offset, count);
        }

        public override void Close()
        {
            _streamToCapture.Close();
            base.Close();
        }
    }

    #region IHttpModule Members

    private HttpApplication _context;
    public void Dispose()
    {

    }

    public void Init(HttpApplication context)
    {
        _context = context;

        context.PreRequestHandlerExecute += new EventHandler(context_PreRequestHandlerExecute);
        context.PreSendRequestContent += new EventHandler(context_PreSendRequestContent);
    }

    void context_PreRequestHandlerExecute(object sender, EventArgs e)
    {
        _context.Response.Filter = new ResponseCaptureStream(_context.Response.Filter, _context.Response.ContentEncoding);
    }

    void context_PreSendRequestContent(object sender, EventArgs e)
    {
        ResponseCaptureStream filter = _context.Response.Filter as ResponseCaptureStream;

        if (filter != null)
        {
            string responseText = filter.StreamContent;

            // Logging logic here
        }
    }

    #endregion
}
person Tom Jelen    schedule 22.12.2008
comment
Это именно то, что мне нужно для stackoverflow.com/ вопросы / 1020045 /. Спасибо! - person jrummell; 23.06.2009
comment
Мне пришлось использовать событие BeginRequest для настройки фильтра, PreRequestHandlerExecute не сработал в моем HttpModule. Я не стал разбираться, почему, но, возможно, это поможет кому-то другому. - person JeremyWeir; 20.10.2010
comment
Замечательное решение проблемы! - person Antony Blazer; 22.12.2010
comment
Отличное решение. Одно из предложений - использовать StringBuilder для StreamContent, а не строку. В противном случае он выделяет новую память для строки и копирует предыдущую строку каждый раз, когда вызывается Write, что будет происходить часто, когда .Net строит вашу страницу. - person ThisGuy; 22.09.2012

Многие тестеры нагрузки позволяют вам регистрировать сгенерированные HTTP-ответы, но имейте в виду, что в ASP.NET это могут быть очень большие файлы журналов.

Изменить: Response.Filter в соответствии с кодом Тома Джелена предназначен для обеспечения такого рода надзора и Response.Outputstream в противном случае нечитаемым.

Изменить 2: для страницы, а не для HTTPModule

public class ObserverStream : Stream
{
  private byte[] buffer = null;
  private Stream observed = null;

  public ObserverStream (Stream s)
  {
    this.observed = s;
  }

  /* important method to extend #1 : capturing the data */
  public override void Write(byte[] buffer, int offset, int count)
  {
    this.observed.Write(buffer, offset, count);
    this.buffer = buffer; //captured!
  }

  /* important method to extend #2 : doing something with the data */
  public override void Close()
  {
    //this.buffer available for logging here!
    this.observed.Close();
  }

  /* override all the other Stream methods/props with this.observed.method() */

  //...

}

и в вашем Page_Load (или до того, как будет написан ваш ответ)

Response.Filter = new ObserverStream(Response.Filter);
person annakata    schedule 22.12.2008
comment
Использование потока Response.OutputStream или Response.Output приводит к возникновению исключения ArgumentException ({System.ArgumentException: поток не читался.) - person lastas; 22.12.2008
comment
Вы сначала стремились к нулю? Записать в читаемый MemStream? (сейчас просто зажигаю VS, потому что я хочу знать, как это сделать) - person annakata; 22.12.2008
comment
Хех, я как раз собирался опубликовать весь код Response.Filter, который я только что написал, и обнаружил, что Том Джелен уже сделал именно это. У меня тоже в принципе работало :) - person annakata; 22.12.2008
comment
Решил, объединив решения annakata и Tom Jelens! Спасибо, парни - person lastas; 23.12.2008

Один из способов сделать серверный XMLHTTP-запрос к вашему собственному серверу. Получите результат и сохраните его в файл или БД.

В качестве альтернативы вы можете использовать AJAX на клиенте, получить результат и отправить его обратно на сервер.

person Diodeus - James MacFarlane    schedule 22.12.2008
comment
удвоение количества запросов кажется плохим, учитывая, что данные есть где-то в первый раз - person annakata; 22.12.2008