2015-04-26 28 views
5

To pytanie zostało zadane wcześniej w kilku postaciach, ale nie mogę uzyskać żadnej z odpowiedzi do pracy, tracę włosy i nie wiem, czy problem polega tylko na tym, że rozwiązania były sprzed 2 lat, a sytuacja się zmieniła.Jak sprawdzić strumień odpowiedzi MVC za pomocą komponentu oprogramowania warstwy pośredniej OWIN?

How can I safely intercept the Response stream in a custom Owin Middleware - Oparłem mój kod na to, że wygląda jak to powinno działać, ale nie

OWIN OnSendingHeaders Callback - Reading Response Body - wydaje się być inną wersję OWIN, ponieważ podpis metoda nie działa

Co chcę zrobić, to napisać OMK, która może kontrolować strumień odpowiedzi z MVC.

Co zrobiłem (wśród kilku innych prób), jest dodanie OMK, który ustawia context.Response.Body do MemoryStream, więc mogę przewinąć go i sprawdzić, co zostało napisane przez dalszych komponentów:

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

Co mogę znaleźć, to że MemoryStream jest zawsze pusty, chyba że piszę do niego z innej OMC. Wygląda więc na to, że kolejne OMC używają mojego MemoryStream, ale odpowiedzi MVC nie są, tak jakby potok OWIN zakończył się zanim zapytanie trafi do MVC, ale to nie jest prawda?

Kompletny kod:

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

    } 

} 

Na co warto Próbowałem też sugestię, gdzie strumień response.Body jest ustawiony na podklasy Stream, tak więc mogłem kontrolować, co jest napisane do strumienia i dziwnie Wywoływana jest metoda Stream.Write, ale z pustą tablicą bajtów, nigdy aktualna treść ...

Odpowiedz

6

MVC nie przekazuje swojego żądania za pośrednictwem potoku OWIN. Aby przechwycić odpowiedź MVC musimy zrobić filtr niestandardowy reagowania, który przechwytuje dane odpowiedzi

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

A potem zrobimy middleware logowania, które mogą logować oba rodzaje odpowiedzi prawidłowo

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