从ASP.NET生成的HTML捕获

18

我该如何最好地捕获aspx页面呈现的HTML(在我的情况下,用于记录)?

我不想使用Response.Write向页面写回,因为它会破坏我的网站布局。

使用Response.OutputStream或Response.Output的流会导致ArgumentException ({System.ArgumentException: Stream was not readable.)


简而言之,重写页面的Render方法。类似的问题和答案请参见此处:https://dev59.com/MkXRa4cB1Zd3GeqPpBxQ - Peter T. LaComb Jr.
3个回答

25

很好的问题,我尝试并想知道是否可以创建一个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
}

这正是我在https://dev59.com/l0jSa4cB1Zd3GeqPCRXk所需的。谢谢! - jrummell
我不得不使用BeginRequest事件来设置过滤器,因为我的HttpModule没有触发PreRequestHandlerExecute事件。我没有深入研究原因,但也许这会帮助其他人。 - JeremyWeir
问题的解决方案太棒了! - Antony Blazer
2
很棒的解决方案。我的建议是,我会使用StringBuilder来代替字符串(string)作为StreamContent。否则,每次“Write”被调用时都会为字符串分配新的内存并复制先前的字符串 - 这将在.Net构建页面时频繁发生。 - ThisGuy

4
许多负载测试工具可以记录生成的HTTP响应,但请记住,在ASP.NET中可能会产生一些非常大的日志文件。
编辑:像Tom Jelen的代码一样,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:Stream was not readable.) - lastas
你是否先尝试将其清零?将其写入可读的MemStream中?(我现在要启动VS,因为我想知道如何做到这一点) - annakata
哈,我正准备发布我刚写的所有Response.Filter代码,结果发现Tom Jelen已经做了这件事。基本上也对我有用 :) - annakata
通过结合annakata和Tom Jelens的解决方案,我解决了这个问题!谢谢你们。 - lastas

1

一种在服务器端发起XMLHTTP请求的方法是向自己的服务器发送请求。获取结果并将其保存到文件或数据库中。

另一种方法是在客户端使用AJAX,获取结果,并将其POST回服务器。


2
重复请求似乎不太好,因为数据第一次就已经存在了。 - annakata

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接