C# IIS:模块的奇怪行为
我有几个遗留网站,每个网站都有很多静态HTML页面。我想使用IIS模块捕获生成的页面内容,并添加额外的HTML片段,使其具有新的页眉和页脚(这称为装饰器模式)。这是我的模块代码。奇怪的是,在许多测试中,我注意到当加载一个页面时,模块被调用两次,每次调用都将页面内容的一部分传递给模块(第一次调用传递页面的顶部,第二次调用传递页面的剩余部分)。我知道模块被调用两次的原因是因为我使用了一个静态变量来捕获调用次数,并将其显示在新的页眉和页脚中(这两个数字不同,页脚数总是比页眉数大1)。我还能够将页面内容导出到两个不同的文件中以证明这一点C# IIS:模块的奇怪行为,c#,iis,iis-modules,C#,Iis,Iis Modules,我有几个遗留网站,每个网站都有很多静态HTML页面。我想使用IIS模块捕获生成的页面内容,并添加额外的HTML片段,使其具有新的页眉和页脚(这称为装饰器模式)。这是我的模块代码。奇怪的是,在许多测试中,我注意到当加载一个页面时,模块被调用两次,每次调用都将页面内容的一部分传递给模块(第一次调用传递页面的顶部,第二次调用传递页面的剩余部分)。我知道模块被调用两次的原因是因为我使用了一个静态变量来捕获调用次数,并将其显示在新的页眉和页脚中(这两个数字不同,页脚数总是比页眉数大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);
}
}
}
以及以下更新的方法:
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 好的,我应该早点解决这个问题。我承认我没有把问题的每一句都读一遍。我应该开始怀疑这个测量。结果是测量结果被破坏了 感谢您提出页面大小是否重要的问题。我又做了一次测试。是的。对于小页面,我在页眉和页脚中看到相同的数字。对于较大的页面,我看到3和4或类似的内容 然后:
Write
可能被调用任意次数。这是一个流
实现。任何人都可以调用Stream。想写多少就写多少。对于任何流
,您都会想到这一点
每页的索引可以增加很多次。计数密码坏了,其余的都能用
此外,UTF-8处理被破坏,因为无法在任意边界分割UTF-8编码的数据。我不知道主要的错误是什么。我建议您放弃过滤方法,而是修改应用程序以直接发出正确的HTML。此通用程序永远无法完全可靠地工作。请修改应用程序以直接发出正确的HTML,这在此时是不可能的。这是一个非常笼统的说法。为什么这么说?让我们在的任何地方进行实验:用Debug.WriteLine(context.GetHashCode())
替换myu包装器。还是每个请求两个电话?让我们缩小问题的范围。不,这不是框架错误或配置问题。这是代码中的一个bug。我们必须找到它因为我使用了一个静态变量来捕获调用次数,并将其显示在新的页眉和页脚中
您可以发布执行此操作的代码吗?老实说,我以前没读过那句话。我的蜘蛛感觉现在很刺痛……任何人都可以调用Stream。想写多久就写多久。
在IIS服务页面的过程中,我的模块的写实现可以被多次调用吗?这本身不是问题。问题是传递给Write的内容不是完整的页面,我希望在添加页眉内容时添加页脚内容。因此,我需要测试是否添加了头,这在第二次调用中传递底部部分时是不可能的。计数代码被破坏了。
。我不明白。你能详细说明一下吗<代码>UTF-8处理中断,因为您无法在任意边界拆分UTF-8编码的数据。
您可以用正确的方式发布吗?非常感谢您发布此答案。好吧,您正在实现Stream
。这意味着调用方将类视为流。他们可以将想要写入的字节发布到任意多的块中。他们可以一次给你一个字节。你必须解决这个问题。例如,您可以将所有内容缓冲在MemoryStream
中,并且只有当您被释放时,您才能解码、修改、编码并写入outputStream
。在Dispose中计数,这将修复计数。UTF8的问题是通过一次解码所有内容来解决的。Encoding.UTF8.GetBytes(“ö”)
为您提供两个字节。但是有人可能会把它们一个一个地写进你的流中。您将尝试解码第一个字节,该字节无效,因为缺少第二个字节。这就是UTF8错误。谢谢你的跟进!最好的。
protected override void Dispose(bool disposing)
{
//code copied from my earlier code, with "buffer" changed to "allContent".
}
public override void Write(byte[] buffer, int offset, int count)
{
//...
index++;