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

发布于 2024-09-19 18:15:15 字数 5296 浏览 10 评论 0原文

我已经研究这个有一段时间了。我发现了有关该主题的几个资源,它们都倾向于使用相同的方法 - 覆盖 Page.Render,使用 HtmlTextWriter 将输出转换为字符串,然后使用一系列编译的正则表达式来删除多余的空格。 这是一个示例

好吧,我尝试了一下,它有效……但是……

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

不管怎样,当你仔细想想,让 .NET 构建所有这些制表符和换行符,然后使用字符串解析再次将它们全部删除,这似乎相当愚蠢——这是删除它们的效率最低的方法。重写 HtmlTextWriter 并将其传递到主页请求上的树中以避免将它们放入输出中会更有意义 - 从逻辑上讲,在这种情况下应该有性能增益而不是命中。

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

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

  1. 将对 WriteLine() 的所有调用定向到相应的 Write() 方法
  2. 将 NewLine 属性设置为空字符串
  3. 覆盖 OutputTabs() 方法并简单地删除代码
  4. 覆盖 Indent 属性和返回 0

我还尝试重写 RenderChildren、Render、BeginRender 和 EndRender 以传入我的自定义 HtmlTextWriter,但我似乎无法使一个简单的标签控件删除其标签之前的选项卡。我还使用 Reflector 挖掘了框架,但我根本无法弄清楚这些字符是如何生成的 - 我以为我使用的是“catch all”方法,但显然我遗漏了一些东西。

无论如何,这就是我的想法。这段代码没有按照我希望的方式运行。当然,我也尝试过直接重写页面上的各种 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 的输出,您会发现他们正在使用某种与此类似的压缩……而且它的性能似乎非常高。

I have been researching this one for awhile. I found several resources on the subject and they all tend to use the same approach - override Page.Render, use the HtmlTextWriter to convert the output into a string, and then use a series of compiled regular expressions to remove the extra whitespace. Here is an example.

Well, I tried it and it works...but....

In Safari 5.0, this seems to cause all sorts of performance issues with loading images and getting "server too busy" errors. IE 7, FireFox 3.6 and Google Chrome 6.0 seem to work okay, but I haven't stress tested the server very much. Occasionally there seems to be lags in the time the page is generated, but once the html is sent to the browser, the page displays quickly.

Anyway, when you think about it, it seems rather silly to have .NET build up all of these tabs and line breaks only to strip them all out again using string parsing - the least efficient way to strip them out. It would make more sense to override the HtmlTextWriter and pass it into the tree on the main page request in order to avoid putting them into the output at all - logically there should be a performance gain instead of a hit in that case.

Even if I can only remove 50% of the whitespace using this method, it will still leave much less work for the regular expressions to do - meaning it should perform better than it does with regular expressions alone.

I tried using a Control Adapter and overriding several of the members

  1. Directing all calls to WriteLine() to the corresponding Write() method
  2. Setting the NewLine property to an empty string
  3. Overriding the OutputTabs() method and simply removing the code
  4. Overriding the Indent property and returning 0

I also tried overriding RenderChildren, Render, BeginRender, and EndRender to pass in my custom HtmlTextWriter, but I can't seem to make even a simple label control remove the tabs before its tag. I also dug through the framework using Reflector, but I simply can't figure out how these characters are being generated - I thought I was using a "catch all" approach, but apparently I am missing something.

Anyway, here is what I have come up with. This code is not functioning the way I would like it to. Of course, I have also tried overriding the various Render methods on the page directly and passing in an instance of my custom HtmlTextWriter, but that didn't work either.

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

In case you are not familliar with control adapters, simply place the xml below in a .browser file in the ASP.NET App_Browsers folder. You can change the controlType to apply the control adapter to a Label or something else for a smaller scope of a test. If I can get this working, it isn't such a big deal to add all of the controls in my project here if it will be necessary to do so.

<browsers>

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

</browsers>

Anyway, you would think there would just be a simple configuration setting like VerboseHtml="false" or PreserveHtmlFormatting="false" or something along those lines. If you look at the output from MSN.com, they are using some kind of compression similar to this...and it appears to be very performant.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

烈酒灼喉 2024-09-26 18:15:15

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

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

最终解决方案

我已经添加了我的最终解决方案到这个要点

The solution involved using a modified version of a compresson module I found here:

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

This solution implements a custom PageParserFilter which is then configured in the web.config file. The beauty of this solution is that it filters the white space at compile time, so at runtime there is no performance hit.

And then thanks to Aristos and diamandiev, I looked for a reasonable way to add compression to the response stream for browsers that support it. I found another open source module that can be used for this here: http://blowery.org/httpcompress/

The performance gain was significant with the combination of the two methods - much more than using one or the other. It seems that browsers render much faster when the tabs are removed from the page.

As for static content, I plan to use the method in this article to compress those files as well (except for images of course, they are already compressed): http://www.lostechies.com/blogs/dahlbyk/archive/2010/01/08/script-to-enable-http-compression-gzip-deflate-in-iis-6.aspx

Final Solution

I have added my final solution to this Gist.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文