2015-04-26 18 views
5

Questa domanda è stata posta in precedenza in alcune forme, ma non riesco a far funzionare nessuna delle risposte, sto perdendo i capelli e non sono sicuro se il problema è solo che le soluzioni erano di 2 anni fa e le cose sono cambiate.Come ispezionare il flusso di risposta MVC utilizzando il componente middleware OWIN?

How can I safely intercept the Response stream in a custom Owin Middleware - ho basato il mio codice su questo, sembra che dovrebbe funzionare, ma non

OWIN OnSendingHeaders Callback - Reading Response Body - sembra essere una versione diversa OWIN, perché il metodo di firma non funziona

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

Quello che ho fatto (fra i numerosi altri tentativi), è quello di aggiungere un metodo aperto di coordinamento che imposta context.Response.Body a un MemoryStream, in modo da poter tornare indietro e controllare ciò che è stato scritto da componenti a valle:

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; 
     ....... 

Quello che trovo è che il MemoryStream è sempre vuoto, a meno che non scriva da un altro OMC. Quindi sembra 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 venisse inviata a MVC, ma non è giusto?

completo del codice:

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<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. 
     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 provato anche un suggerimento in cui il torrente response.Body è impostato su una sottoclasse Stream, solo così ho potuto monitorare ciò che viene scritto nel flusso e stranamente la Viene chiamato il metodo Stream.Write, ma con un array di byte vuoto, mai alcun contenuto effettivo ...

risposta

6

MVC non trasmette la sua richiesta tramite la pipeline OWIN. Per catturare risposta MVC abbiamo bisogno di fare filtro di risposta personalizzato che cattura i dati di risposta

/// <summary> 
/// Stream capturing the data going to another stream 
/// </summary> 
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); 
    } 
} 

E poi facciamo un middleware di registrazione in grado di registrare entrambi i tipi di risposte correttamente

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