简体   繁体   English

如何使用OWIN中间件组件检查MVC响应流?

[英]How to inspect MVC response stream using OWIN middleware component?

This question has been asked before in a few forms but I cannot get any of the answers to work, I'm losing my hair and unsure if the problem is just that the solutions were from 2 years ago and things have changed. 这个问题之前已经被提出了几个问题,但是我无法得到任何工作的答案,我正在失去我的头发并且不确定问题是否只是解决方案来自2年前并且事情已经发生了变化。

How can I safely intercept the Response stream in a custom Owin Middleware - I based my code on this, it looks like it should work, but it doesn't 我如何安全地拦截自定义Owin中间件中的响应流 - 我基于此我的代码,它看起来应该工作,但它不

OWIN OnSendingHeaders Callback - Reading Response Body - seems to be a different OWIN version, because method signature doesn't work OWIN OnSendingHeaders回调 - 读取响应主体 - 似乎是一个不同的OWIN版本,因为方法签名不起作用

What I want to do is write an OMC that can inspect the response stream from MVC. 我想要做的是编写一个可以检查来自MVC的响应流的OMC。

What I did (amongst several other attempts), is to add an OMC that sets context.Response.Body to a MemoryStream, so I can rewind it and inspect what was written by downstream components: 我做的(在其他几次尝试中)是添加一个OMC,它将context.Response.Body设置为MemoryStream,所以我可以回放它并检查下游组件写的内容:

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

What I find is that the MemoryStream is always empty, unless I write to it from another OMC. 我发现MemoryStream总是空的,除非我从另一个OMC写入它。 So it seems that downstream OMCs are using my MemoryStream, but MVC responses are not, as if the OWIN pipeline completes before the request goes to MVC, but that's not right is it? 所以看起来下游的OMC正在使用我的MemoryStream,但是MVC响应不是,好像OWIN管道在请求进入MVC之前完成了,但是这不对吗?

Complete code: 完整代码:

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

    }

}

For what it's worth I also tried a suggestion where the response.Body stream is set to a Stream subclass, just so I could monitor what is written to the stream and bizarrely the Stream.Write method is called, but with an empty byte array, never any actual content... 为了它的价值,我还尝试了一个建议,其中response.Body流被设置为Stream子类,这样我就可以监视写入流的内容,并且奇怪地调用了Stream.Write方法,但是使用空字节数组,从来没有任何实际内容

MVC does not pass its request through OWIN pipeline. MVC不通过OWIN管道传递其请求。 To capture MVC response we need to make custom response filter that captures response data 要捕获MVC响应,我们需要制作捕获响应数据的自定义响应过滤器

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

And then we make a logging middleware that can log both kinds of responses properly 然后我们制作一个记录中间件,可以正确记录这两种响应

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

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM