IIS:模块的奇怪行为

IIS: Odd behavior of modules

我有几个旧网站,每个网站都有很多静态 HTML 页面。我想使用 IIS 模块来捕获生成的页面内容并添加额外的 HTML 片段以使其具有新的页眉和页脚(这称为装饰器模式)。这是我为模块准备的代码。奇怪的是,在许多测试中,我注意到加载页面时模块被调用 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 的宝贵意见,"odd" 行为只是 IIS 的正常行为。由于 his/her 建议,我添加了一个 class 变量:

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".
    }

现在一切正常!谢谢你,usr!!!

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

Thanks for asking the question about whether the page size matters. I did tests again. It does. For small pages, I see the same number in header and footer. For large pages, I see 3 and 4 or something like that.

然后:

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

        index++;

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

每页索引可以递增多次。计数码坏了,剩下的还行

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