如何在自定义的Owin中间件中安全地拦截Response流 [英] How can I safely intercept the Response stream in a custom Owin Middleware

查看:194
本文介绍了如何在自定义的Owin中间件中安全地拦截Response流的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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

I'm trying to write a simple OWIN Middleware, in order to intercept the response stream. What I'm trying to do is replace the original stream with custom Stream-based class, where I will be able to intercept writes to the response stream.

但是,我遇到了一些问题,因为我不知道响应何时由链中的内部中间件组件完全写入.永远不会调用Stream的Dispose替代.因此,我不知道何时该执行我的处理,该处理应该在响应流的末尾发生.

However, I'm facing some issues because I cannot know when the response has been completely written to by inner middleware components in the chain. The Dispose override of the Stream is never called. So I don't know when it's time to perform my processing, which should happen at the end of the response Stream.

这是示例代码:

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

正如我在上面代码的注释中提到的那样,我可以考虑两种策略来检测响应是否完整.

As I've alluded to in the comments from the code above, there are two strategies that I can think of in order to detect whether the response is complete.

a)我可以记录写入响应流的字节数,并将其与期望的响应长度相关联.但是,对于使用分块传输编码的响应,长度是未知的.

a) I can record the number of bytes written to the response stream and correlate that to the expected response length. However, in the case of responses which use the Chunked Transfer Encoding, the length is not known.

b)当在响应流上调用Dispose时,我可以确定响应流已完成.但是,OWIN/Katana基础设施从不对替换后的流调用Dispose.

b) I can decide that the response stream is complete when Dispose is called on the response stream. However, the OWIN/Katana infrastructure never calls Dispose on the replaced stream.

我一直在调查不透明流,以查看是否操纵了基本的HTTP协议将是一种可行的方法,但是我似乎没有发现Katana是否支持不透明流.

I have been investigating Opaque Streaming in order to see whether manipulating the underlying HTTP protocol would be a feasible approach, but I don't seem to find whether Katana supports Opaque Streaming or not.

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

Is there a way to achieve what I want ?

推荐答案

我认为您不需要子类化的流,但是下面是读取响应的方法.只需确保该中间件是OWIN管道中的第一个中间件,以使其成为检查响应的最后一个即可.

I do not think you will need a sub-classed stream but then here is how you can read the response. Just ensure this middleware is the first one in the OWIN pipeline so that it will be the last one to inspect the response.

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

据我所知,

顺便说一句,从OwinMiddleware派生不是一个好习惯,因为OwinMiddleware是特定于Katana的.但是,这与您的问题无关.

BTW, as far as I know, deriving from OwinMiddleware is not considered a good practice because OwinMiddleware is specific to Katana. It is however nothing to do with your problem though.

这篇关于如何在自定义的Owin中间件中安全地拦截Response流的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆