在ASP.NET 2.0中,如何从呈现的页面/控件输出中删除额外的选项卡和换行符?

时间:2010-09-15 21:26:10

标签: asp.net html render htmltextwriter control-adapter

我一直在研究这个。我找到了关于这个主题的几个资源,他们都倾向于使用相同的方法 - 覆盖Page.Render,使用HtmlTextWriter将输出转换为字符串,然后使用一系列编译的正则表达式来删除额外的空格。 Here is an example

嗯,我尝试了它并且它有效...但是......

在Safari 5.0中,这似乎会导致加载图像和“服务器太忙”错误时出现各种性能问题。 IE 7,FireFox 3.6和谷歌Chrome 6.0似乎工作正常,但我没有对服务器进行过多的压力测试。有时在页面生成时似乎存在滞后,但是一旦将html发送到浏览器,页面就会快速显示。

无论如何,当你考虑它时,让.NET构建所有这些选项卡和换行符只是为了使用字符串解析再次将它们全部删除 - 这是剥离它们的最有效方法,这似乎相当愚蠢。覆盖HtmlTextWriter并将其传递到主页面请求中的树更有意义,以避免将它们放入输出中 - 逻辑上应该有性能增益而不是那种情况下的命中。

即使我只能使用这种方法删除50%的空白,它仍然会使正则表达式的工作少得多 - 这意味着它应该比单独使用正则表达式更好。

我尝试使用控制适配器并覆盖了几个成员

  1. 将对WriteLine()的所有调用指向相应的Write()方法
  2. 将NewLine属性设置为空字符串
  3. 覆盖OutputTabs()方法并简单地删除代码
  4. 覆盖Indent属性并返回0
  5. 我还尝试重写RenderChildren,Render,BeginRender和EndRender以传递我的自定义HtmlTextWriter,但我似乎无法使一个简单的标签控件在其标记之前删除标签。我还使用Reflector挖掘了框架,但我根本无法弄清楚这些字符是如何生成的 - 我以为我使用的是“全部捕获”方法,但显然我错过了一些东西。

    无论如何,这是我想出的。此代码无法按照我希望的方式运行。当然,我也尝试直接覆盖页面上的各种Render方法并传入我的自定义HtmlTextWriter的实例,但这也不起作用。

    Public Class PageCompressorControlAdapter
        Inherits System.Web.UI.Adapters.ControlAdapter
    
        Protected Overrides Sub RenderChildren(ByVal writer As System.Web.UI.HtmlTextWriter)
            MyBase.RenderChildren(New CompressedHtmlTextWriter(writer))
        End Sub
    
        Protected Overrides Sub Render(ByVal writer As System.Web.UI.HtmlTextWriter)
            MyBase.Render(New CompressedHtmlTextWriter(writer))
        End Sub
    
        Protected Overrides Sub BeginRender(ByVal writer As System.Web.UI.HtmlTextWriter)
            MyBase.BeginRender(New CompressedHtmlTextWriter(writer))
        End Sub
    
        Protected Overrides Sub EndRender(ByVal writer As System.Web.UI.HtmlTextWriter)
            MyBase.EndRender(New CompressedHtmlTextWriter(writer))
        End Sub
    
    End Class
    
    Public Class CompressedHtmlTextWriter
        Inherits HtmlTextWriter
    
        Sub New(ByVal writer As HtmlTextWriter)
            MyBase.New(writer, "")
            Me.InnerWriter = writer.InnerWriter
    
            Me.NewLine = ""
        End Sub
    
        Sub New(ByVal writer As System.IO.TextWriter)
            MyBase.New(writer, "")
            MyBase.InnerWriter = writer
    
            Me.NewLine = ""
        End Sub
    
        Protected Overrides Sub OutputTabs()
            'Skip over the tabs
        End Sub
    
        Public Overrides Property NewLine() As String
            Get
                Return ""
            End Get
            Set(ByVal value As String)
                MyBase.NewLine = value
            End Set
        End Property
    
    
        Public Overrides Sub WriteLine()
    
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As Boolean)
            MyBase.Write(value)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As Char)
            MyBase.Write(value)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal buffer() As Char)
            MyBase.Write(buffer)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal buffer() As Char, ByVal index As Integer, ByVal count As Integer)
            MyBase.Write(buffer, index, count)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As Decimal)
            MyBase.Write(value)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As Double)
            MyBase.Write(value)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As Integer)
            MyBase.Write(value)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As Long)
            MyBase.Write(value)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As Object)
            MyBase.Write(value)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As Single)
            MyBase.Write(value)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal s As String)
            MyBase.Write(s)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal format As String, ByVal arg0 As Object)
            MyBase.Write(format, arg0)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal format As String, ByVal arg0 As Object, ByVal arg1 As Object)
            MyBase.Write(format, arg0, arg1)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal format As String, ByVal arg0 As Object, ByVal arg1 As Object, ByVal arg2 As Object)
            MyBase.Write(format, arg0, arg1, arg2)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal format As String, ByVal ParamArray arg() As Object)
            MyBase.Write(format, arg)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As UInteger)
            MyBase.Write(value)
        End Sub
    
        Public Overrides Sub WriteLine(ByVal value As ULong)
            MyBase.Write(value)
        End Sub
    
    End Class
    

    如果您不熟悉控件适配器,只需将下面的xml放在ASP.NET App_Browsers文件夹中的.browser文件中即可。您可以更改controlType以将控件适配器应用于Label或其他内容以用于较小的测试范围。如果我可以使用它,那么在我的项目中添加所有控件并不是一件大事,如果有必要的话。

    <browsers>
    
        <browser refID="Default">
            <controlAdapters>
                <adapter controlType="System.Web.UI.Page"
                         adapterType="PageCompressorControlAdapter"/>
            </controlAdapters>
        </browser>
    
    </browsers>
    

    无论如何,你会认为只有一个简单的配置设置,如VerboseHtml =“false”或PreserveHtmlFormatting =“false”或类似的东西。如果你看看MSN.com的输出,他们正在使用某种类似于此的压缩...而且它似乎非常高效。

1 个答案:

答案 0 :(得分:1)

解决方案涉及使用我在此处找到的压缩模块的修改版本:

http://omari-o.blogspot.com/2009/09/aspnet-white-space-cleaning-with-no.html

此解决方案实现了一个自定义PageParserFilter,然后在web.config文件中进行配置。这个解决方案的优点在于它在编译时过滤了空白区域,因此在运行时没有性能损失。

然后感谢Aristos和diamandiev,我寻找一种合理的方法来为支持它的浏览器添加压缩到响应流。我找到了另一个可用于此的开源模块:http://blowery.org/httpcompress/

两种方法相结合,性能提升显着 - 远远超过使用其中一种方法。当从页面中删除选项卡时,浏览器似乎渲染得更快。

对于静态内容,我打算使用本文中的方法来压缩这些文件(当然,图像已被压缩):http://www.lostechies.com/blogs/dahlbyk/archive/2010/01/08/script-to-enable-http-compression-gzip-deflate-in-iis-6.aspx

最终解决方案

我已将最终解决方案添加到this Gist