【问题标题】:IIS: Odd behavior of modulesIIS:模块的奇怪行为
【发布时间】:2016-07-09 12:26:18
【问题描述】:

我有几个旧网站,每个网站都有很多静态 HTML 页面。我想使用 IIS 模块来捕获生成的页面内容并添加额外的 HTML sn-ps 以使其具有新的页眉和页脚(这称为装饰器模式)。这是我的模块代码。奇怪的是,在许多测试中,我注意到在加载页面时会调用模块 TWICE 并且每次调用都会将页面的部分内容传递给模块(第一次调用通过顶部页面的一部分,第二个页面的剩余部分)。我知道模块被调用两次的原因是因为我使用了一个静态变量来捕获调用次数并将其显示在新的页眉和页脚中(两个数字不同,页脚编号始终比页眉编号大 1)。我还能够将页面内容导出到两个不同的文件中来证明这一点。

namespace MyProject
{
    public class MyModule : IHttpModule
    {
        public void Dispose()
        {
        }

        public void Init(HttpApplication application)
        {
            application.ReleaseRequestState += new EventHandler(this.My_Wrapper);
        }

        public String ModuleName
        {
            get { return "MyProject"; }
        }

        public void My_Wrapper(Object source, EventArgs e)
        {
            HttpApplication app = (HttpApplication)source;
            HttpContext context = app.Context;
            HttpRequest request = context.Request;
            string requestPath = request.Path.ToString();

            //I have guarding code here so that the following code only applies to 
            //web requests that has ".html" in the end.

            HttpContext.Current.Response.Filter = new WrapperFilter(HttpContext.Current.Response.Filter);
        }
    }

    public class WrapperFilter : MemoryStream
    {
        private static Regex startOfBody = new Regex("(?i)<body(([^>])*)>", RegexOptions.Compiled | RegexOptions.Multiline);
        private static Regex endOfBody = new Regex("(?i)</body>", RegexOptions.Compiled | RegexOptions.Multiline);

        private Stream outputStream = null;

        private static int index = 0;

        public WrapperFilter(Stream output)
        {
            outputStream = output;
        }

        public override void Write(byte[] buffer, int offset, int count)
        {
            string contentInBuffer = UTF8Encoding.UTF8.GetString(buffer);
            string page = new StringBuilder(contentInBuffer).ToString();
            byte[] outputBuffer = null;
            Match matchStartOfBody = null;
            Match matchEndOfBody = null;

            index++;

            matchStartOfBody = startOfBody.Match(page);
            string header = "html snippets for header: " + index;
            page = startOfBody.Replace(page, "<body " + matchStartOfBody.Groups[1] + ">" + header);

            matchEndOfBody = endOfBody.Match(page); 
            string footer = "html snippets for footer: " + index;
            page = endOfBody.Replace(page, footer + "</body>");

            outputBuffer = UTF8Encoding.UTF8.GetBytes(page);
            outputStream.Write(outputBuffer, 0, outputBuffer.Length);
        }
    }
}

问题:

  1. 模块加载两次是因为页面内容太大还是需要增加缓存?如果有,怎么做?

  2. 从技术上讲,我的方法行得通吗?我能够装饰 HTML 页面,并且由于两个调用过程,我无法处理一些高级情况。

  3. 当需要在浏览器页面中显示图像,并且对图像的请求通过 IIS 模块时?

更新

根据来自 usr 的宝贵意见,“奇怪”行为只是 IIS 的正常行为。由于他/她的建议,我添加了一个类变量:

private byte[] allContent = new byte[0];

以及以下更新的方法:

    public override void Write(byte[] buffer, int offset, int count)
    {
        //new bigger array
        byte[] newArr = new byte[allContent.Length + buffer.Length];
        //copy old content
        System.Array.Copy(allContent, newArr, allContent.Length);
        //append new content
        System.Array.Copy(buffer, 0, newArr, allContent.Length, buffer.Length);
        //reset current total content
        allContent = newArr;
    }

并使用从我之前的 Write 方法中复制的所有代码添加一个新方法:

    protected override void Dispose(bool disposing)
    {
    //code copied from my earlier code, with "buffer" changed to "allContent".
    }

现在一切正常!谢谢楼主!!!

【问题讨论】:

  • 我不知道主要的错误是什么。我建议您放弃过滤器方法,而是修改应用程序以直接发出正确的 HTML。这个泛型永远不会完全可靠地工作。
  • 修改应用程序以直接发出正确的 HTML,目前这是不可能的。 这个泛型永远不会完全可靠地工作 这是一个非常笼统的陈述。为什么这么说?
  • 将处理出现&lt;body 字符串的任何位置。可能是向 JS 文字发出的用户输入。或者,它可能是他现在下载的用户上传的文件,并且恰好包含该字符串。或者恰好包含这些字节的图像。真的很难做到这一点,它限制了你可以编写的应用程序的种类。反正就是提这个。我没有解决方案。
  • 让我们进行实验:将My_Wrapper 替换为Debug.WriteLine(context.GetHashCode())。每个请求仍然需要两次调用?让我们缩小问题的范围。
  • 不,这不是框架错误或配置问题。这是您的代码中的一个错误。我们必须找到它。 because I used a static variable to capture the number of invocation and show it in the new header and footer你能贴出这样的代码吗?老实说,我以前没有读过那句话。我的蜘蛛感现在正在刺痛....

标签: c# iis iis-modules


【解决方案1】:

好的,我应该早点解决这个问题。我承认我没有阅读问题的每一句话。我应该对测量产生怀疑。原来测量被破坏了。

感谢您提出有关页面大小是否重要的​​问题。我又做了测试。确实如此。对于小页面,我在页眉和页脚中看到相同的数字。对于大页面,我看到 3 和 4 或类似的东西。

然后:

    public override void Write(byte[] buffer, int offset, int count)
    {
        //...

        index++;

Write 可能被调用任意次数。这是一个Stream 实现。任何人都可以随时拨打Stream.Write。你会期望任何Stream.

索引可以每页增加多次。计数代码被破坏,其余的工作。

此外,UTF-8 处理被破坏,因为您无法在任意边界分割 UTF-8 编码数据。

【讨论】:

  • Anyone can call Stream.Write as often as he wants to.你的MY模块的write实现在IIS服务一个页面的过程中会被多次调用吗?这本身不是问题。问题是传递给 Write 的内容不是 COMPLETE 页面,我希望在添加页眉内容时添加页脚内容。所以我需要测试是否添加了header,这在第二次调用中通过底部时是不可能的。
  • The counting code is broken。我不明白这一点。你能详细说明一下吗? the UTF-8 processing is broken because you can't split UTF-8 encoded data at arbitrary boundaries. 能发一下正确的方法吗?非常感谢您发布此答案。
  • 嗯,你正在实现Stream。这意味着调用者将您的类视为流。他们可以将想要写入的字节发布到任意数量的块中。他们可以一次给你一个字节。你必须处理那个。例如,您可以缓冲MemoryStream 中的所有内容,并且只有在您被处理时,您才能解码、修改、编码和写入outputStream。在 Dispose 中计数,修复计数。通过一次解码所有内容来修复 UTF8 问题。
  • Encoding.UTF8.GetBytes("ö") 给你两个字节。但是有人可能会将它们一一写入您的流中。您将尝试解码由于第二个字节丢失而无效的第一个字节。这就是这里的 UTF8 错误。
  • 感谢您的跟进!最好的。
猜你喜欢
  • 2022-11-14
  • 2021-11-08
  • 2021-02-27
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多