在ASP.NET MVC管道中替换文本的最佳位置

5

版本2

我更新了原始代码,考虑到write方法以块的形式从页面流式传输HTML。

正如指出的“由于无法保证在write中连续写入“THE_PLACEHOLDER”。你可能会在一次write调用的末尾得到“THE_PLACEH”,并在下一次调用的开头得到“OLDER”。

我通过将流的完整内容放入Stringbuilder中,并在Close方法上执行任何所需的更新来解决了这个问题。

这样做后,我再次提出以下问题....


我正在开发一个CMS,它只是用CMS文本替换占位符。

我有以下代码,它的功能正常。

我已经重写了IHttpModule。

public class CmsFilterHttpModule : IHttpModule {

  // In the Init method, register HttpApplication events by adding event handlers.
  public void Init( HttpApplication httpApplication ) {

    httpApplication.ReleaseRequestState += new EventHandler( this.HttpApplication_OnReleaseRequestState );

  }

  /// <summary>
  /// HttpApplication_OnReleaseRequestState event handler.
  /// 
  /// Occurs after ASP.NET finishes executing all request event handlers. 
  /// This event causes state modules to save the current state data.
  /// </summary>
  private void HttpApplication_OnReleaseRequestState( Object sender, EventArgs e ) {

    HttpResponse httpResponse = HttpContext.Current.Response;

    if ( httpResponse.ContentType == "text/html" ) {

      httpResponse.Filter = new CmsFilterStream( httpResponse.Filter );

    }

  }

  public void Dispose() {

    //Empty

  }

} 

以及MemoryStream

public class CmsFilterStream : MemoryStream {

  private Stream        _responseStream;  
  private StringBuilder _responseHtml;   

  public CmsFilterStream( Stream inputStream ) {

    _responseStream = inputStream;
    _responseHtml = new StringBuilder();

  }

  /// <summary>
  ///   Writes a block of bytes to the current stream using data read from a buffer.
  /// </summary>
  /// <param name="buffer">The buffer to write data from.</param>
  /// <param name="offset">The zero-based byte offset in buffer at which to begin copying bytes to the current stream.</param>
  /// <param name="count">The maximum number of bytes to write.</param>
  public override void Write( Byte[] buffer, Int32 offset, Int32 count ) {

    if ( buffer == null ) { throw new ArgumentNullException( "buffer", "ArgumentNull_Buffer" ); }
    if ( offset < 0 ) { throw new ArgumentOutOfRangeException( "offset", "ArgumentOutOfRange_NeedNonNegNum" ); }
    if ( count < 0 ) { throw new ArgumentOutOfRangeException( "count", "ArgumentOutOfRange_NeedNonNegNum" ); }
    if ( buffer.Length - offset < count ) { throw new ArgumentException( "Argument_InvalidOffLen" ); }

    String bufferContent = UTF8Encoding.UTF8.GetString( buffer, offset, count );

    _responseHtml.Append( bufferContent );

  }

  public override void Close() {

    _responseHtml.Replace( "THE_PLACEHOLDER", "SOME_HTML" );

    _responseStream.Write( UTF8Encoding.UTF8.GetBytes( _responseHtml.ToString() ), 0, UTF8Encoding.UTF8.GetByteCount( _responseHtml.ToString() ) );

    _responseStream.Dispose();

    base.Close();

  }

}

并且在 Web.config 文件中添加以下内容

<system.webServer>
  <modules>
    <remove name="CmsFilterHttpModule" />
    <add name="CmsFilterHttpModule" type="{MY_NAMESPACE}.CmsFilterHttpModule" />
  </modules>
</system.webServer>

这符合我的要求。

我的问题是,在开始向后工作之前,这是最佳的管道位置吗?

此方法将替换已完成的输出中的文本。

我正在寻找从管道角度最快的替换此文本的方法。

暂时忽略String.Replace / Stringbuilder和其他各种方法的速度优化。我认为优化稍微往后进行。

我尚未完全调试整个管道,但我猜测页面必须由不同的部分(即布局、视图部分等)构建。也许在这些部分替换文本更快。

此外,是否会出现任何问题?

String bufferContent = UTF8Encoding.UTF8.GetString(buffer);

当使用其他语言,如日语、中文等时。

我还必须说明,我正在尝试将此作为一个单独的附加代码来实现,尽可能少地触及用户站点MVC代码。


2
你有研究过"OnActionExecuted"动作过滤器吗? - Alex
我还必须补充说明,我正在尝试将此作为一个独立的附加代码来完成,尽可能少地触及原始MVC代码。因此,这就排除了Action过滤器的使用。 - William Humphreys
这是正确的方法;以前做过这个。但是,你的写入可能存在错误,因为不能保证在写入中“THE_PLACEHOLDER”被连续地写入字节块。你可能会在一个写入调用的末尾得到“THE_PLACEH”,并在下一个调用的开头得到“OLDER”。 - bloudraak
编写一个状态机 :) - bloudraak
经过进一步测试,您是正确的,该流以16368字节的块进行处理。 - William Humphreys
显示剩余4条评论
1个回答

2

为了在不必处理调用写入方法时的状态之间的混乱的情况下处理完整响应,您的实现无需覆盖Write方法,只需覆盖Close方法即可,因为您首先需要捕获所有字节,然后再进行转换。以下是一个可行的实现:

public class CmsFilterStream : MemoryStream {

    private Stream        _responseStream;  

    public CmsFilterStream( Stream inputStream ) {
        _responseStream = inputStream;
    }

    public override void Close() {
        var allHtml = UTF8Encoding.UTF8.GetString(this.ToArray()); // get ALL bytes!!
        allHtml = allHtml.Replace("THE_PLACEHOLDER", "SOME_HTML");

        var buf =UTF8Encoding.UTF8.GetBytes(allHtml);
        _responseStream.Write(buf,0, buf.Length);

        _responseStream.Flush(); // I assume the caller will close the _responseStream

        base.Close();
    }
}

这是一个天真的实现。你可以优化替换代码和写入流,但我只会在性能测量表明此代码块处于热路径时才进行优化。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接