簡體   English   中英

如何在HttpModule中執行XSLT轉換?

[英]How can I perform XSLT transformations in an HttpModule?

我一直在嘗試將服務器端XSLT轉換實現為IIS HttpModule。 我的基本方法是在BeginRequest上安裝一個新的過濾器,將寫入轉移到MemoryStream,然后在PreSendRequestContent上使用XSLT轉換文檔並將其寫入原始輸出流。 但是,即使沒有執行轉換,我顯然做錯了,因為HttpModule似乎適用於第一頁加載,然后在我重新啟動應用程序池之前我根本沒有得到服務器的響應。 隨着轉換到位,我第一次得到一個空頁,然后沒有響應。 我顯然做了一些愚蠢的事情,但這是我多年來寫的第一個C#代碼(我第一次嘗試HttpModule),我不知道問題可能是什么。 我犯了什么錯誤? (我在下面的代碼中注釋掉了XSLT部分,並取消注釋了一條將緩存內容寫入響應的行。)

using System;
using System.IO;
using System.Text;
using System.Web;
using System.Xml;
using System.Xml.Xsl;

namespace Onyx {

    public class OnyxModule : IHttpModule {

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

        public void Dispose() {
        }

        public void Init(HttpApplication application) {

            application.BeginRequest += (sender, e) => {
                HttpResponse response = HttpContext.Current.Response;
                response.Filter = new CacheFilter(response.Filter);
                response.Buffer = true;
            };

            application.PreSendRequestContent += (sender, e) => {

                HttpResponse response = HttpContext.Current.Response;
                CacheFilter cache = (CacheFilter)response.Filter;

                response.Filter = cache.originalStream;
                response.Clear();

 /*               XmlReader xml = XmlReader.Create(new StreamReader(cache), new XmlReaderSettings() {
                    ProhibitDtd = false,
                    ConformanceLevel = ConformanceLevel.Auto
                });

                XmlWriter html = XmlWriter.Create(response.OutputStream, new XmlWriterSettings() {
                    ConformanceLevel = ConformanceLevel.Auto
                });

                XslCompiledTransform xslt = new XslCompiledTransform();
                xslt.Load("http://localhost/transformations/test_college.xsl", new XsltSettings() {
                    EnableDocumentFunction = true
                }, new XmlUrlResolver());
                xslt.Transform(xml, html); */

                response.Write(cache.ToString());

                response.Flush();

            };

        }


    }

    public class CacheFilter : MemoryStream {

        public Stream originalStream;
        private MemoryStream cacheStream;

        public CacheFilter(Stream stream) {
            originalStream = stream;
            cacheStream = new MemoryStream();
        }

        public override int Read(byte[] buffer, int offset, int count) {
            return cacheStream.Read(buffer, offset, count);
        }

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

        public override bool CanRead {
            get { return cacheStream.CanRead; }
        }

        public override string ToString() {
            return Encoding.UTF8.GetString(cacheStream.ToArray());
        }

    }

}

完成將數據讀入MemoryStream后,位置位於流的末尾。 在將流發送到StreamReader / XmlReader之前,您需要將位置重置為0。

stream.Position = 0;
/* or */
stream.Seek(0, SeekOrigin.Begin);

我對它的工作有點驚訝(即使在重置了流的位置之后)。 我稍微探討了HttpApplication代碼,盡管我還沒有完全理解它,但看起來你可能在請求處理過程中太晚修改了輸出流。

如果您還沒有想到這一點,請嘗試將第二個處理函數附加到PostReleaseRequestState之后的事件PostReleaseRequestState - UpdateRequestCachePostUpdateRequestCache 兩種聲音都不是特別合適,但請繼續閱讀!

出於某種原因, HttpApplicationMSDN文檔在其事件列表中不包含PreSendRequestContent ,但Reflector顯示在HttpResponse.Flush之前不會調用其處理程序。

如果我正在讀取正確的代碼, Response.Flush 調用處理程序之前計算內容長度,因此它認為在獲得此代碼時響應為空:

if (contentLength > 0L) {
    byte[] bytes = Encoding.ASCII.GetBytes(Convert.ToString(contentLength, 0x10) + "\r\n");
    this._wr.SendResponseFromMemory(bytes, bytes.Length);
    this._httpWriter.Send(this._wr);
    this._wr.SendResponseFromMemory(s_chunkSuffix, s_chunkSuffix.Length);
}

根據您的入口點和初始條件,可能會調用一些備用路徑,這可能解釋了為什么這種情況在某些時候起作用而在其他時間不起作用。 但是在一天結束時,一旦你進入Flush你可能不應該修改響應流。

你正在做一些有點不尋常的事情 - 你並沒有真正過濾傳統意義上的響應流(你將一些字節傳遞給另一個流),所以你可能需要做一些有點hackish來使你當前的設計工作。

另一種方法是使用IHttpHandler而不是模塊來實現它 - 這里有一個很好的例子 它涉及從數據庫查詢轉換輸出,但應該很容易適應文件系統數據源。

即使您不遵守msdn示例,也應該實現HttpApplication.EndRequest

context.EndRequest += (sender, e) => {
    HttpResponse response = HttpContext.Current.Response;
    response.Flush();
};

清潔器

// ...

public void Init(HttpApplication application)
{
    // ...
    application.EndRequest += (new EventHandler(this.Application_EndRequest));
}

private void Application_EndRequest(object sender, EventArgs e)
{
    HttpApplication application = (HttpApplication)source;
    HttpContext context = application.Context;
    context.Current.Response.Flush();
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM