[英]ASP.NET Response.Filter: on the entire content/full response
我正在尝试创建响应过滤器,以修改由另一个Web应用程序生成的html。
如下所示,我创建了一个从Stream
派生的Response.Filter
。
Response.Filter
以块的形式编写,如果响应大于单个块,则最终会陷入混乱。
在Write
,内容被缓存到另一个MemoryStream
以缓冲所有块。
在Flush
,现在在MemoryStream
的所有内容都将转换为字符串,并且过滤逻辑开始。
然后将修改后的内容写回原始流。
可同时处理整个内容的Response.Filter
的正确实现是什么?
HttpModule.cs
:
using System;
using System.Web;
namespace ACME
{
public class HttpModule : IHttpModule
{
void IHttpModule.Init(HttpApplication context)
{
context.BeginRequest += ContextBeginRequest;
}
private void ContextBeginRequest(object sender, EventArgs e)
{
HttpApplication app = sender as HttpApplication;
if (app != null)
{
if ( app.Request.FilePath.ToLower().EndsWith(".aspx") )
{
app.Response.Filter = new ACMEFilter(app.Response.Filter);
}
}
}
void IHttpModule.Dispose()
{
// Nothing to dispose;
}
}
}
ACMEFilter.cs
:
using System;
using System.IO;
using System.Web;
using System.Text;
using System.Text.RegularExpressions;
namespace ACME
{
internal class ACMEFilter: Stream
{
private readonly Stream _outputStream;
private MemoryStream _cachedStream = new MemoryStream(1024);
public override bool CanRead
{
get { return true; }
}
public override bool CanSeek
{
get { return true; }
}
public override bool CanWrite
{
get { return true; }
}
public override long Length
{
get { return 0; }
}
public override long Position { get; set; }
public ACMEFilter(Stream stream)
{
_outputStream = stream;
}
public override void Flush()
{
Encoding encoding = HttpContext.Current.Response.ContentEncoding;
string content = encoding.GetString(_cachedStream.ToArray());
// filter logic here!
content += "";
byte[] buffer = encoding.GetBytes(content);
_cachedStream = new MemoryStream();
_cachedStream.Write(buffer, 0, buffer.Length);
_outputStream.Write(_cachedStream.ToArray(), 0, (int)_cachedStream.Length);
_cachedStream.SetLength(0);
_outputStream.Flush();
}
public override int Read(byte[] buffer, int offset, int count)
{
return _cachedStream.Read(buffer, offset, count);
}
public override long Seek(long offset, SeekOrigin origin)
{
return _cachedStream.Seek(offset, origin);
}
public override void SetLength(long value)
{
_cachedStream.SetLength(value);
}
public override void Close()
{
_cachedStream.Close();
}
public override void Write(byte[] buffer, int offset, int count)
{
_cachedStream.Write(buffer, offset, count);
}
}
}
web.config
:
<system.webServer>
<modules>
<add name="ACME" type="ACME.HttpModule, ACME" preCondition="managedHandler" />
</modules>
<system.webServer>
可同时处理整个内容的Response.Filter的正确实现是什么?
根据定义,流不能一次处理全部文本/字节。
另外,在流上可能多次调用Flush()
,因此在此方法中写入标头是无效的,因为它可能最终将它们多次放入响应中。
app.Response.Filter = new ACMEFilter(app.Response.Filter);
上面的代码似乎实现了装饰器模式(这在流中很常见),但是您发布的实现没有构造函数。 通常,您将要写入的流传递给构造函数以对其进行包装,然后重写所有方法并写入内部流实例。 这些覆盖的方法是您需要放置自定义逻辑以按所需方式写入输出的地方,并根据需要存储任何状态以标记流的不同部分以进行自定义。
internal class ACMEFilter : Stream
{
private readonly Stream _innerStream;
public ACMEFilter(Stream innerStream)
{
_innerStream = innerStream;
}
public override void Write(byte[] buffer, int offset, int count)
{
// Implement custom filter logic here,
// storing any flags as class-level variables as needed
// to act as switches across many calls to this and other
// methods of the stream.
_innerStream.Write(buffer, offset, count);
}
// Other member overrides...
}
有一篇文章更深入地解释了如何创建响应过滤器https://weblog.west-wind.com/posts/2009/Nov/13/Capturing-and-Transforming-ASPNET-Output-with-ResponseFilter涵盖了以下事实:在应用程序提供时,流数据需要分块处理,并提供了一种替代方法。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.