如何安全地拦截自定义Owin中间件中的响应流

我正在尝试编写一个简单的OWIN中间件,以拦截响应流。 我想要做的是用自定义的基于Stream的类替换原始流,在那里我将能够拦截对响应流的写入。

但是,我遇到了一些问题,因为我无法知道响应是否已被链中的内部中间件组件完全写入。 永远不会调用Stream的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)我可以记录写入响应流的字节数,并将其与预期的响应长度相关联。 但是,在使用Chunked Transfer Encoding的响应的情况下,长度是未知的。

b)在响应流上调用Dispose时,我可以决定响应流是否完整。 但是,OWIN / Katana基础结构从不在已替换的流上调用Dispose。

我一直在研究Opaque Streaming ,看看操纵底层的HTTP协议是否是一种可行的方法,但我似乎没有发现Katana是否支持Opaque Streaming。

有没有办法实现我想要的?

我不认为您需要一个子类流,但接下来就是您如何阅读响应。 确保这个中间件是OWIN管道中的第一个,这样它就是最后一个检查响应的中间件。

 using AppFunc = Func, Task>; public class CustomMiddleware { private readonly AppFunc next; public CustomMiddleware(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. 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并不是一个好习惯,因为OwinMiddleware是专门针对Katana的。 然而,它与你的问题无关。