Как мне лучше всего захватить HTML (в моем случае для ведения журнала), отображаемый aspx-страницей?
Я не хочу, чтобы мне приходилось писать обратно на страницу с помощью Response.Write, поскольку это портит макет моего сайта.
Использование потока Response.OutputStream или Response.Output приводит к возникновению исключения ArgumentException ({System.ArgumentException: поток не читался.)





Многие тестеры нагрузки позволяют вам регистрировать сгенерированные 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);
Использование потока Response.OutputStream или Response.Output приводит к возникновению исключения ArgumentException ({System.ArgumentException: поток не читался.)
Вы сначала стремились к нулю? Записать в читаемый MemStream? (сейчас просто зажигаю VS, потому что я хочу знать, как это сделать)
Хех, я как раз собирался опубликовать весь код Response.Filter, который я только что написал, и обнаружил, что Том Джелен уже сделал именно это. У меня тоже в принципе работало :)
Решил, объединив решения annakata и Tom Jelens! Спасибо, парни
Один из способов сделать серверный XMLHTTP-запрос к вашему собственному серверу. Получите результат и сохраните его в файл или БД.
В качестве альтернативы вы можете использовать AJAX на клиенте, получить результат и отправить его обратно на сервер.
удвоение количества запросов кажется плохим, учитывая, что данные есть где-то в первый раз
Хороший вопрос, мне пришлось попробовать и посмотреть, смогу ли я создать 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
}
Это как раз то, что мне нужно для stackoverflow.com/questions/1020045/…. Спасибо!
Мне пришлось использовать событие BeginRequest для настройки фильтра, PreRequestHandlerExecute не сработал в моем HttpModule. Я не стал разбираться, почему, но, возможно, это поможет кому-то другому.
Замечательное решение проблемы!
Отличное решение. Одно из предложений - использовать StringBuilder для StreamContent, а не строку. В противном случае он выделяет новую память для строки и копирует предыдущую строку каждый раз, когда вызывается "Write", что будет происходить часто, когда .Net строит вашу страницу.
Короче говоря, переопределите метод Render для страницы. Аналогичный вопрос и ответы здесь