2010-12-16 9 views
8

Como mencioné en un par de otras publicaciones (ver Referencias a continuación) Estoy intentando crear filtros de respuesta para modificar contenido producido por otro Aplicación web.Cómo implemento un filtro de respuesta HTTP para operar en todo el contenido a la vez, sin fragmentar

Tengo la lógica básica de transformación de cadenas funcionando y encapsulada en Filtros que se derivan de un FilterBase común. Sin embargo, la lógica debe operar en todo el contenido, no en trozos de contenido. Por lo tanto, necesito almacenar en caché los fragmentos a medida que se escriben y realizar el filtro cuando se completen todas las escrituras.

Como se muestra a continuación, creé un nuevo filtro de respuesta derivado de MemoryStream. En Escribir, el contenido se almacena en caché en otro MemoryStream. En Flush, el contenido completo, ahora en MemoryStream, se convierte en una cadena y la lógica del Filtro se activa. El contenido modificado se vuelve a escribir en la secuencia de origen.

Sin embargo, en cada segunda solicitud (básicamente cuando un nuevo filtro se crea una instancia sobre el anterior) se está ejecutando el método de lavado del filtro anterior. En este punto, la aplicación se bloquea en el método _outputStream.Write() ya que _cachedStream está vacío.

El orden de eventos es la siguiente:

  1. Primera Solicitud
  2. método Write se llama
  3. método Flush se llama
  4. método Close se llama
  5. método Close se llama
  6. En este punto, la aplicación vuelve y se muestra el contenido adecuado.
  7. Segunda Solicitud
  8. método Flush se llama
  9. aplicación se bloquea en _outputStream.Write. ArgumentOutOfRangeException (desplazamiento).
  10. continuar a través de accidente (w/en Visual Studio)
  11. método Close se llama

hay un par de preguntas que tengo:

  1. Por qué está cerca llamado dos veces?
  2. ¿Por qué se llama a Flush después de llamar a Cerrado?
  3. Al siguiente punto de Jay, se puede llamar a Flush antes de que la transmisión sea completamente leída, ¿dónde debería residir la lógica del filtro? En Close? En color pero con "si se cierra"?
  4. ¿Cuál es la implementación adecuada para un filtro de respuesta que funciona en todo el contenido a la vez?

Nota: Voy a experimentar el mismo comportamiento exacto (menos eventos Close) si no anular el método 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); 

Referencias:

Respuesta

0

Flush no está siendo llamado explícitamente. Tal vez se invoca cuando el código se da cuenta de que se necesita un nuevo objeto, o quizás como resultado de un finalizador.

Creo que uno puede llamar a color después de cualquier escritura incremental, por lo que no estoy seguro de que una llamada a color sea una indicación adecuada de un mensaje completo de todos modos.

+0

Incluso si se invoca implícitamente como resultado de un finalizador, esperaría que _cachedContent contenga los datos que se escribieron en él. Sin embargo, _cachedContent parece no contener datos. Buen punto sobre el uso de color para escrituras incrementales. –

7

Gracias a una sugerencia de Jay sobre el llamado a Flush para las escrituras incrementales, he podido hacer que el filtro funcione como lo desee realizando la lógica de filtrado solo si el filtro se está cerrando y aún no se ha cerrado. Esto asegura que el filtro solo se vacía una vez cuando se cierra el flujo. Logré esto con unos pocos campos simples, _isClosing y _isClosed como se muestra en el código final a continuación.

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

que aún no han encontrado respuestas a mis otras preguntas anteriores, así que no va a marcar esta respuesta como exceptuados en este momento.

+0

Esta debería haber sido la respuesta: me ayudó a implementar un filtro de respuesta usando HtmlAgilityPack para modificar atributos en nodos html. ¡Gracias! –

+1

No entiendo por qué en la anulación del método de Escritura siempre pone 0 como el desplazamiento? – Durden81

+1

+1. Y estoy interesado en tus clases 'FilterBase' y' MapServiceJsonResponseFilter' ... – jerone

Cuestiones relacionadas