net example c# stream owin katana owin-middleware

c# - example - ¿Cómo puedo interceptar de forma segura el flujo de Respuesta en un Owin Middleware personalizado?



owin authentication (1)

Estoy tratando de escribir un OWIN Middleware simple, para interceptar el flujo de respuesta. Lo que estoy tratando de hacer es reemplazar la secuencia original con una clase personalizada basada en la secuencia, donde podré interceptar las escrituras de la secuencia de respuesta.

Sin embargo, me enfrento a algunos problemas porque no puedo saber cuándo los componentes de middleware interno de la cadena escribieron la respuesta por completo. La anulación de disposición de la secuencia nunca se llama. Por lo tanto, no sé cuándo es el momento de realizar mi procesamiento, lo que debería ocurrir al final de la secuencia de respuesta.

Aquí hay un código de ejemplo:

public sealed class CustomMiddleware: OwinMiddleware { public CustomMiddleware(OwinMiddleware next) : base(next) { } public override async Task Invoke(IOwinContext context) { var request = context.Request; var response = context.Response; // capture response stream var vr = new MemoryStream(); var responseStream = new ResponseStream(vr, response.Body); response.OnSendingHeaders(state => { var resp = (state as IOwinContext).Response; var contentLength = resp.Headers.ContentLength; // contentLength == null for Chunked responses }, context); // invoke the next middleware in the pipeline await Next.Invoke(context); } } public sealed class ResponseStream : Stream { private readonly Stream stream_; // MemoryStream private readonly Stream output_; // Owin response private long writtenBytes_ = 0L; public ResponseStream(Stream stream, Stream output) { stream_ = stream; output_ = output; } ... // System.IO.Stream implementation public override void Write(byte[] buffer, int offset, int count) { // capture writes to the response stream in our local stream stream_.Write(buffer, offset, count); // write to the real output stream output_.Write(buffer, offset, count); // update the number of bytes written writtenBytes_ += count; // how do we know the response is complete ? // we could check that the number of bytes written // is equal to the content length, but content length // is not available for Chunked responses. } protected override void Dispose(bool disposing) { // we could perform our processing // when the stream is disposed of. // however, this method is never called by // the OWIN/Katana infrastructure. } }

Como mencioné en los comentarios del código anterior, hay dos estrategias en las que puedo pensar para detectar si la respuesta es completa.

a) Puedo registrar el número de bytes escritos en el flujo de respuesta y correlacionarlos con la longitud de respuesta esperada. Sin embargo, en el caso de las respuestas que utilizan la codificación de transferencia fragmentada, la longitud no se conoce.

b) Puedo decidir que la secuencia de respuesta esté completa cuando se llame a Dispose en la secuencia de respuesta. Sin embargo, la infraestructura OWIN / Katana nunca llama a Desechar en el flujo reemplazado.

He estado investigando Opaque Streaming para ver si la manipulación del protocolo HTTP subyacente sería un enfoque factible, pero no parece encontrar si Katana es compatible con Opaque Streaming o no.

¿Hay alguna manera de lograr lo que quiero?


No creo que necesites una secuencia sub-clasificada, pero aquí es cómo puedes leer la respuesta. Solo asegúrese de que este middleware sea el primero en la línea de OWIN para que sea el último en inspeccionar la respuesta.

using AppFunc = Func<IDictionary<string, object>, Task>; public class CustomMiddleware { private readonly AppFunc next; public CustomMiddleware(AppFunc next) { this.next = next; } public async Task Invoke(IDictionary<string, object> env) { IOwinContext context = new OwinContext(env); // Buffer the response var stream = context.Response.Body; var buffer = new MemoryStream(); context.Response.Body = buffer; await this.next(env); buffer.Seek(0, SeekOrigin.Begin); var reader = new StreamReader(buffer); string responseBody = await reader.ReadToEndAsync(); // Now, you can access response body. Debug.WriteLine(responseBody); // You need to do this so that the response we buffered // is flushed out to the client application. buffer.Seek(0, SeekOrigin.Begin); await buffer.CopyToAsync(stream); } }

Por cierto, por lo que sé, derivar de OwinMiddleware no se considera una buena práctica porque OwinMiddleware es específico de Katana. Sin embargo, no tiene nada que ver con tu problema.