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:
- Primera Solicitud
- método Write se llama
- método Flush se llama
- método Close se llama
- método Close se llama
- En este punto, la aplicación vuelve y se muestra el contenido adecuado.
- Segunda Solicitud
- método Flush se llama
- aplicación se bloquea en _outputStream.Write. ArgumentOutOfRangeException (desplazamiento).
- continuar a través de accidente (w/en Visual Studio)
- método Close se llama
hay un par de preguntas que tengo:
- Por qué está cerca llamado dos veces?
- ¿Por qué se llama a Flush después de llamar a Cerrado?
- 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"?
- ¿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:
- How do I deploy a managed HTTP Module Site Wide?
- Creating multiple (15+) HTTP Response filters, Inheritance vs. Composition w/ Injection
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. –