代码之家  ›  专栏  ›  技术社区  ›  Rich

如何在httpmodule中执行XSLT转换?

  •  3
  • Rich  · 技术社区  · 14 年前

    我一直在尝试将服务器端的XSLT转换作为一个IISHttpModule来实现。我的基本方法是在BeginRequest安装一个新的过滤器,它将写入的内容转移到MemoryStream中,然后在PresentRequestContent中使用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());
            }
    
        }
    
    }
    
    3 回复  |  直到 14 年前
        1
  •  3
  •   Mikael Svenson    14 年前

    当您将数据读取到您的内存流时,该位置位于流的末尾。在将流发送到streamreader/xmlreader之前,需要将位置重置为0。

    stream.Position = 0;
    /* or */
    stream.Seek(0, SeekOrigin.Begin);
    
        2
  •  2
  •   Jeff Sternal    14 年前

    我有点惊讶这项工作在所有(即使在重置流的位置之后)。我到处找 HttpApplication 有点代码,虽然我不完全理解它,但在请求处理过程中,您可能修改输出流太晚了。

    如果您仍然没有弄清楚,请尝试将第二个处理程序函数附加到后面的某个事件 PostReleaseRequestState -要么 UpdateRequestCache PostUpdateRequestCache . 这两个听起来都不太合适,但请继续阅读!

    出于某种原因, MSDN documentation for HttpApplication 不包括 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 .

    您正在做一些有点不寻常的事情—您并不是在传统意义上过滤响应流(您将一些字节传递给另一个流),所以您可能需要做一些有点黑客的事情来使您当前的设计工作正常。

    另一种选择是使用 IHttpHandler 而不是模块-有 a good example here . 它处理来自数据库查询的转换输出,但应该很容易适应文件系统数据源。

        3
  •  1
  •   Filburt kukabuka    14 年前

    即使你不坚持 msdn example 你应该实施 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();
    }