Come ispezionare il stream di risposta MVC utilizzando il componente middleware OWIN?

Questa domanda è stata posta in precedenza in alcune forms, ma non riesco a far funzionare nessuna delle risposte, sto perdendo i capelli e non so se il problema è solo che le soluzioni erano di 2 anni fa e le cose sono cambiate.

Come posso intercettare in modo sicuro lo stream di risposta in un Owin Middleware personalizzato – Ho basato il mio codice su questo, sembra che dovrebbe funzionare, ma non lo fa

OWIN OnSendingHeaders Callback – Reading Response Body – sembra essere una versione OWIN diversa, perché la firma del metodo non funziona

Quello che voglio fare è scrivere un OMC in grado di ispezionare il stream di risposta da MVC.

Quello che ho fatto (tra molti altri tentativi), è quello di aggiungere un OMC che imposta context.Response.Body su MemoryStream, così posso riavvolgerlo e ispezionare ciò che è stato scritto dai componenti downstream:

public async Task Invoke(IDictionary env) { IOwinContext context = new OwinContext(env); // Buffer the response var stream = context.Response.Body; var buffer = new MemoryStream(); context.Response.Body = buffer; ....... 

Quello che trovo è che il MemoryStream è sempre vuoto, a meno che non scriva ad esso da un altro OMC. Sembra quindi che gli OMC downstream utilizzino il mio MemoryStream, ma le risposte MVC non lo sono, come se la pipeline OWIN fosse completata prima che la richiesta arrivasse a MVC, ma non è giusto?

Codice completo:

 public partial class Startup { public void Configuration(IAppBuilder app) { ConfigureAuth(app); app.Use(new ResponseExaminerMiddleware()); // Specify the stage for the OMC //app.UseStageMarker(PipelineStage.Authenticate); } } public class ResponseExaminerMiddleware { private AppFunc next; public void Initialize(AppFunc next) { this.next = next; } public async Task Invoke(IDictionary 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. System.Diagnostics.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); } } 

Per quel che vale, ho anche provato un suggerimento in cui la risposta.Il stream di stream è impostato su una sottoclass Stream, solo così potrei monitorare ciò che è scritto nello stream e in modo bizzarro viene chiamato il metodo Stream.Write, ma con una matrice di byte vuota, mai alcun contenuto reale …

MVC non trasmette la sua richiesta tramite la pipeline OWIN. Per acquisire la risposta MVC, è necessario creare un filtro di risposta personalizzato che acquisisca i dati di risposta

 ///  /// Stream capturing the data going to another stream ///  internal class OutputCaptureStream : Stream { private Stream InnerStream; public MemoryStream CapturedData { get; private set; } public OutputCaptureStream(Stream inner) { InnerStream = inner; CapturedData = new MemoryStream(); } public override bool CanRead { get { return InnerStream.CanRead; } } public override bool CanSeek { get { return InnerStream.CanSeek; } } public override bool CanWrite { get { return InnerStream.CanWrite; } } public override void Flush() { InnerStream.Flush(); } public override long Length { get { return InnerStream.Length; } } public override long Position { get { return InnerStream.Position; } set { CapturedData.Position = InnerStream.Position = value; } } public override int Read(byte[] buffer, int offset, int count) { return InnerStream.Read(buffer, offset, count); } public override long Seek(long offset, SeekOrigin origin) { CapturedData.Seek(offset, origin); return InnerStream.Seek(offset, origin); } public override void SetLength(long value) { CapturedData.SetLength(value); InnerStream.SetLength(value); } public override void Write(byte[] buffer, int offset, int count) { CapturedData.Write(buffer, offset, count); InnerStream.Write(buffer, offset, count); } } 

Quindi realizziamo un middleware di registrazione in grado di registrare correttamente entrambi i tipi di risposta

 public class LoggerMiddleware : OwinMiddleware { public LoggerMiddleware(OwinMiddleware next): base(next) { } public async override Task Invoke(IOwinContext context) { //to intercept MVC responses, because they don't go through OWIN HttpResponse httpResponse = HttpContext.Current.Response; OutputCaptureStream outputCapture = new OutputCaptureStream(httpResponse.Filter); httpResponse.Filter = outputCapture; IOwinResponse owinResponse = context.Response; //buffer the response stream in order to intercept downstream writes Stream owinResponseStream = owinResponse.Body; owinResponse.Body = new MemoryStream(); await Next.Invoke(context); if (outputCapture.CapturedData.Length == 0) { //response is formsd by OWIN //make sure the response we buffered is flushed to the client owinResponse.Body.Position = 0; await owinResponse.Body.CopyToAsync(owinResponseStream); } else { //response by MVC //write captured data to response body as if it was written by OWIN outputCapture.CapturedData.Position = 0; outputCapture.CapturedData.CopyTo(owinResponse.Body); } LogResponse(owinResponse); } }