[英]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.