2014-10-06 18 views
19

Tôi đang cố gắng viết một phần mềm trung gian OWIN đơn giản để chặn luồng phản hồi. Những gì tôi đang cố gắng làm là thay thế luồng gốc bằng lớp dựa trên luồng tùy chỉnh, nơi tôi có thể chặn ghi vào luồng phản hồi.Làm cách nào tôi có thể chặn luồng phản hồi một cách an toàn trong một tùy chỉnh Owin Middleware

Tuy nhiên, tôi đang gặp một số vấn đề bởi vì tôi không thể biết khi nào phản hồi đã được viết hoàn toàn bởi các thành phần trung gian bên trong trong chuỗi. Ghi đè Dispose của Luồng không bao giờ được gọi. Vì vậy, tôi không biết khi nào là thời gian để thực hiện quá trình xử lý của tôi, điều này sẽ xảy ra ở cuối Luồng phản hồi.

Dưới đây là một số mẫu mã:

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

Như tôi đã nhắc đến trong các ý kiến ​​từ các mã trên, có hai chiến lược mà tôi có thể nghĩ ra để phát hiện xem phản ứng hoàn tất.

a) Tôi có thể ghi lại số byte được ghi vào luồng phản hồi và tương quan với độ dài phản hồi dự kiến. Tuy nhiên, trong trường hợp các câu trả lời sử dụng Mã hóa Chuyển Chunked, độ dài không được biết.

b) Tôi có thể quyết định rằng luồng phản hồi hoàn thành khi Dispose được gọi trên luồng phản hồi. Tuy nhiên, cơ sở hạ tầng OWIN/Katana không bao giờ gọi Dispose trên luồng thay thế.

Tôi đã điều tra Opaque Streaming để xem liệu thao tác giao thức HTTP cơ bản có phải là một cách tiếp cận khả thi hay không, nhưng dường như tôi không tìm thấy liệu Katana có hỗ trợ phát trực tuyến hay không.

Có cách nào để đạt được những gì tôi muốn không?

Trả lời

34

Tôi không nghĩ rằng bạn sẽ cần luồng được phân loại phụ nhưng sau đó là cách bạn có thể đọc câu trả lời. Chỉ cần đảm bảo phần mềm trung gian này là phần đầu tiên trong đường ống OWIN sao cho nó sẽ là phần cuối cùng để kiểm tra phản hồi.

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, như xa như tôi biết, xuất phát từ OwinMiddleware không được xem là một thực hành tốt vì OwinMiddleware là cụ thể cho Katana. Tuy nhiên nó không có gì để làm với vấn đề của bạn.

+1

Cảm ơn bạn đã trả lời. Điểm lấy về bắt nguồn từ 'OwinMiddleware'. Tuy nhiên, trong phản ứng của bạn, bạn đang sử dụng 'OwinContext' cũng là đặc trưng cho Katana. Tui bỏ lỡ điều gì vậy ? –

+0

Có, bạn là chính xác. Nhưng tôi đang sử dụng 'OwinContext' nội bộ vào phần mềm trung gian, có nghĩa là tôi đang phụ thuộc vào Katana. Tuy nhiên bằng cách không sử dụng nó trong hàm tạo hoặc các ký hiệu phương thức 'Invoke', tôi không ép buộc các assembly khác phải phụ thuộc vào Katana. Bất cứ ai xây dựng đường ống OWIN không cần biết bất cứ điều gì về 'OwinMiddleware'. Tương tự như vậy, khi một phần mềm trung gian trước đây trong đường ống này gọi là 'Gọi'. – Badri

+0

Tôi quản lý để tìm một liên kết đến một câu trả lời SO từ David Fowler liên quan đến điều này. http://stackoverflow.com/a/19613529/1709870 – Badri

Các vấn đề liên quan