2014-10-06 5 views
19

응답 스트림을 가로 채기 위해 간단한 OWIN 미들웨어를 작성하려고합니다. 내가하고 싶은 것은 원래 스트림을 사용자 지정 스트림 기반 클래스로 대체하는 것입니다. 여기서 응답 스트림에 대한 쓰기를 가로 챌 수 있습니다.사용자 정의 Owin Middleware에서 응답 스트림을 안전하게 가로 챌 수있는 방법

그러나 체인의 내부 미들웨어 구성 요소가 응답을 완전히 기록한 시점을 알 수 없기 때문에 일부 문제에 직면하고 있습니다. 스트림의 Dispose 재정의는 절대로 호출되지 않습니다. 따라서 응답 처리가 끝날 때 처리가 수행되는 시점을 언제 알 수 없습니다.

나는 위의 코드에서 코멘트에 언급했듯이
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. 
    } 
} 

, 내가 응답이 완료 여부를 감지하기 위해 생각할 수있는 두 가지 전략이있다 : 여기

은 샘플 코드입니다.

a) 응답 스트림에 기록 된 바이트 수를 기록하고 예상 응답 길이와 상관시킬 수 있습니다. 그러나 청크 분할 전송 인코딩을 사용하는 응답의 경우 길이는 알 수 없습니다.

b) Dispose이 응답 스트림에서 호출 될 때 응답 스트림이 완료되었다고 결정할 수 있습니다. 그러나 OWIN/Katana 인프라는 교체 된 스트림에서 Dispose를 호출하지 않습니다.

내가 기본 HTTP 프로토콜을 조작하는 것이 실현 가능한 접근법인지 여부를 확인하기 위해 Opaque Streaming을 조사했지만 Katana가 Opaque Streaming을 지원하는지 여부를 찾지 못하는 것 같습니다.

내가 원하는 것을 얻을 수있는 방법이 있습니까?

답변

34

하위 분류 된 스트림이 필요하지 않겠지 만 응답을 읽는 방법은 여기에 있습니다. 이 미들웨어가 OWIN 파이프 라인의 첫 번째 것이므로 응답을 검사하는 것이 가장 마지막 일 것입니다. OwinMiddleware이 카타나에 해당하기 때문에

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

BTW, 내가 아는 한, OwinMiddleware에서 파생 것은 좋은 연습으로 간주되지 않습니다. 그러나 그것은 당신의 문제와는 아무 상관이 없습니다.

+1

답변 해 주셔서 감사합니다. 'OwinMiddleware'에서 파생 된 점. 그러나 응답에서 Katana에 고유 한 OwinContext를 사용하고 있습니다. 내가 놓친 게 있니? –

+0

네, 맞습니다. 하지만 미들웨어에 내부적으로 OwinContext를 사용하고 있는데, 이는 Katana에 의존하고 있음을 의미합니다. 그러나 생성자 또는'Invoke' 메소드 시그니처에서이를 사용하지 않기 때문에 다른 어셈블리가 Katana에 의존하지 않도록합니다. OWIN 파이프 라인을 만드는 사람은 누구나 OwinMiddleware에 대해 알 필요가 없습니다. 비슷하게 파이프 라인에있는 미들웨어가 'Invoke'를 호출 할 때 마찬가지입니다. – Badri

+0

David Fowler의 SO 대답에 대한 링크가 있습니다. http://stackoverflow.com/a/19613529/1709870 – Badri