如何实现HTTP响应过滤器以立即对整个内容进行操作,无需分块

Rya*_*lor 8 c# flush stream httpresponse filter

正如其他几篇文章中所述(请参阅下面的参考资料)我试图创建响应过滤器,以便修改由另一个Web应用程序生成的内容.

我将基本的字符串转换逻辑工作并封装到源自公共FilterBase的Filters中.但是,逻辑必须对完整内容进行操作,而不是对内容进行操作.因此,我需要在写入时缓存块,并在完成所有写入时执行过滤器.

如下所示,我创建了一个从MemoryStream派生的新ResponseFilter.在写入时,内容缓存到另一个MemoryStream.在Flush上,现在在MemoryStream中的完整内容将转换为字符串,并且Filter逻辑将启动.然后将修改后的内容写回原始流.

但是,在每个第二个请求(基本上当一个新的过滤器在前一个过滤器上实例化)时,正在执行前一个过滤器的Flush方法.此时,由于_cachedStream为空,应用程序在_outputStream.Write()方法上崩溃.

活动顺序如下:

  1. 第一次请求
  2. 调用Write方法
  3. 调用Flush方法
  4. 调用Close方法
  5. 调用Close方法
  6. 此时,应用程序返回并显示正确的内容.
  7. 第二个请求
  8. 调用Flush方法
  9. 应用程序在_outputStream.Write上崩溃.ArgumentOutOfRangeException(offset).
  10. 继续崩溃(在Visual Studio中)
  11. 调用Close方法

我有几个问题:

  1. 为什么Close叫两次?
  2. 为什么在关闭后调用Flush?
  3. 对于Jay的观点,可以在完全读取流之前调用Flush,过滤器逻辑应该驻留在哪里?在关闭?在Flush但是"如果关闭"?
  4. 对一次处理整个内容的响应过滤器的正确实现是什么?

注意:如果我不重写Close方法,我会遇到完全相同的行为(减去Close事件).

public class ResponseFilter : MemoryStream
{
    private readonly Stream _outputStream;
    private MemoryStream _cachedStream = new MemoryStream(1024);

    private readonly FilterBase _filter;

    public ResponseFilter (Stream outputStream, FilterBase filter)
    {
        _outputStream = outputStream;
        _filter = filter;
    }

    // Flush is called on the second, fourth, and so on, page request (second request) with empty content.
    public override void Flush()
    {
        Encoding encoding = HttpContext.Current.Response.ContentEncoding;

        string cachedContent = encoding.GetString(_cachedStream.ToArray());

        // Filter the cached content
        cachedContent = _filter.Filter(cachedContent);

        byte[] buffer = encoding.GetBytes(cachedContent);
        _cachedStream = new MemoryStream();
        _cachedStream.Write(buffer, 0, buffer.Length);

        // Write new content to stream
        _outputStream.Write(_cachedStream.ToArray(), 0, (int)_cachedStream.Length);
        _cachedStream.SetLength(0);

        _outputStream.Flush();
    }

    // Write is called on the first, third, and so on, page request.
    public override void Write(byte[] buffer, int offset, int count)
    {
        // Cache the content.
        _cachedStream.Write(buffer, 0, count);
    }

    public override void Close()
    {
        _outputStream.Close();
    }
}

// Example usage in a custom HTTP Module on the BeginRequest event.
FilterBase transformFilter = new MapServiceJsonResponseFilter();
response.Filter = new ResponseFilter(response.Filter, transformFilter);
Run Code Online (Sandbox Code Playgroud)

参考文献:

Rya*_*lor 7

感谢来自周杰伦关于冲洗小费通过执行仅如果过滤器关闭过滤逻辑需要的和尚未闭合被称为增量写我已经能够使过滤器的工作.这可确保在Stream关闭时,Filter仅刷新一次.我用几个简单的字段_isClosing和_isClosed完成了这个,如下面的最终代码所示.

public class ResponseFilter : MemoryStream
{
    private readonly Stream _outputStream;
    private MemoryStream _cachedStream = new MemoryStream(1024);

    private readonly FilterBase _filter;
    private bool _isClosing;
    private bool _isClosed;

    public ResponseFilter (Stream outputStream, FilterBase filter)
    {
        _outputStream = outputStream;
        _filter = filter;
    }

    public override void Flush()
    {
        if (_isClosing && !_isClosed)
        {
            Encoding encoding = HttpContext.Current.Response.ContentEncoding;

            string cachedContent = encoding.GetString(_cachedStream.ToArray());

            // Filter the cached content
            cachedContent = _filter.Filter(cachedContent);

            byte[] buffer = encoding.GetBytes(cachedContent);
            _cachedStream = new MemoryStream();
            _cachedStream.Write(buffer, 0, buffer.Length);

            // Write new content to stream
            _outputStream.Write(_cachedStream.ToArray(), 0, (int)_cachedStream.Length);
            _cachedStream.SetLength(0);

            _outputStream.Flush();
        }
    }

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

    public override void Close()
    {
        _isClosing = true;

        Flush();

        _isClosed = true;
        _isClosing = false;

        _outputStream.Close();
    }
}
Run Code Online (Sandbox Code Playgroud)

我还没有找到上述其他问题的答案,所以我不会在此时将此答案标记为例外.


小智 0

未显式调用 Flush。也许当代码意识到需要一个新对象时,或者作为终结器的结果,它会被调用。

我认为可以在任何增量写入之后调用刷新,因此我不确定对刷新的调用是否足以表明完整的消息。