捕获从 ASP.NET 生成的 HTML

Posted

技术标签:

【中文标题】捕获从 ASP.NET 生成的 HTML【英文标题】:Capturing HTML generated from ASP.NET 【发布时间】:2010-09-28 00:58:12 【问题描述】:

如何最好地捕获由 aspx 页面呈现的 html(在我的实例中,用于日志记录)?

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

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

【问题讨论】:

简而言之,覆盖页面的 Render 方法。类似问答here 【参考方案1】:

好问题,我必须尝试一下,看看是否可以创建一个 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

【讨论】:

这正是我所需要的***.com/questions/1020045/…。谢谢! 我不得不使用 BeginRequest 事件来设置过滤器,PreRequestHandlerExecute 没有在我的 HttpModule 中触发。我没有调查原因,但也许这会对其他人有所帮助。 这个问题的惊人解决方案! 很好的解决方案。一个建议是我会为 StreamContent 使用 StringBuilder 而不是字符串。否则,它会为字符串分配新的内存并在每次调用“Write”时复制前一个字符串——这会在 .Net 构建您的页面时发生很多。【参考方案2】:

许多负载测试器允许您记录生成的 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.) 您是否首先寻求归零?写入可读的 MemStream? (现在我自己启动 VS,因为我想知道该怎么做) 嘿,我正要发布我刚刚编写的所有 Response.Filter 代码,发现 Tom Jelen 已经这样做了。基本上也为我工作:) 通过结合 annakata 和 Tom Jelens 解决方案解决了这个问题!谢谢大家【参考方案3】:

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

您也可以在客户端使用 AJAX,获取结果并将其 POST 回服务器。

【讨论】:

鉴于数据第一次就在某处,因此请求加倍似乎很糟糕

以上是关于捕获从 ASP.NET 生成的 HTML的主要内容,如果未能解决你的问题,请参考以下文章

如何使用 C# 和 ASP.NET 从网页中截取 div?

ASP.NET MVC 控制器不会从 LINQ lambda 捕获异常

将图像捕获照片从电话间隙传输到 C# asp.net Web 服务

asp.net mvc 3 和动态视图生成

在 ASP.NET 应用程序中捕获所有异常的最佳方法是啥?

从 asp.net 中的字节生成 pdf [关闭]