在asp.net mvc pipline中替换文本的最佳位置

时间:2022-06-07 04:07:19

VERSION 2

I have updated the original code taking into account the fact that the write method streamed the HTML from the page in chunks.

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

As pointed out 'Since you're not guaranteed to have "THE_PLACEHOLDER" being written in a contiguous block of bytes in write. You may get "THE_PLACEH" at the end of one call to write, and "OLDER" at the beginning of the next.

正如所指出的那样'因为您不能保证在写入时将“THE_PLACEHOLDER”写入连续的字节块中。您可以在一次写入结束时获得“THE_PLACEH”,并在下一次开始时获得“OLDER”。

I have fixed this by putting the complete content of the stream in a Stringbuilder and doing any update that is required on the Close method.

我已经通过将流的完整内容放在Stringbuilder中并执行Close方法所需的任何更新来解决此问题。

Having done this I am asking the same question again below....

这样做后,我再次在下面问同样的问题....


I'm working on a CMS that simply replaces a placeholder with the CMS text.

我正在使用CMS来简单地用CMS文本替换占位符。

I have the following which is working as it should.

我有以下工作正常工作。

I have overridden the IHttpModule

我已经覆盖了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

  }

} 

and the MemoryStream

和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();

  }

}

and the following in the Web.config

以及Web.config中的以下内容

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

This does work as I require.

这确实有效。

My question really is this the best place in the pipeline to do this before I start to work backwards.

我的问题是,在我开始倒退之前,这是在管道中做到这一点的最佳位置。

This method is replacing text on the completed output.

此方法正在替换已完成输出上的文本。

I'm looking for the fastest way to replace this text from the pipeline perspective.

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

For the moment ignoring the speed of String.Replace / Stringbuilder and the various other methods. I see that optimization slightly further on.

目前忽略了String.Replace / Stringbuilder的速度以及其他各种方法。我进一步看到了优化。

I haven't debugged through the whole pipeline yet but though I'm guessing this the page must be being built from different parts i.e. layouts, views partial etc etc. maybe its faster to replace the text at these parts.

我还没有通过整个管道进行调试,但是我猜这个页面必须是从不同的部分构建的,即布局,视图部分等等,可能更快地替换这些部分的文本。

Also in addition will there be any issues with

另外还有任何问题

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

when using other languages Japanese, Chinese etc.

当使用其他语言日语,中文等

I also must add that I'm trying to do this as a separate added on piece of code that touches the users site MVC code as little as possible.

我还必须补充说,我正在尝试将此作为单独添加的代码,尽可能少地触及用户站点MVC代码。

1 个解决方案

#1


1  

To handle the complete response without the fuzz of having to deal with state between calls to write your implementation doesn't need to override the Write method, only the Close method is needed because you first need to capture ALL bytes before converting. This is an implementation that works:

要处理完整的响应而不需要处理写入实现的调用之间的状态,不需要覆盖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();
    }
}

This is a naive implementation. You can optimize the replacement code and writing to the stream but I would only optimize that if your performance measurements indicate that this peace of code is on the hot path.

这是一个天真的实现。您可以优化替换代码并写入流,但是如果您的性能测量结果表明代码的安静在热门路径上,我只会优化它。

#1


1  

To handle the complete response without the fuzz of having to deal with state between calls to write your implementation doesn't need to override the Write method, only the Close method is needed because you first need to capture ALL bytes before converting. This is an implementation that works:

要处理完整的响应而不需要处理写入实现的调用之间的状态,不需要覆盖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();
    }
}

This is a naive implementation. You can optimize the replacement code and writing to the stream but I would only optimize that if your performance measurements indicate that this peace of code is on the hot path.

这是一个天真的实现。您可以优化替换代码并写入流,但是如果您的性能测量结果表明代码的安静在热门路径上,我只会优化它。