问题描述
这个问题以前曾以几种形式被问过,但我无法得到任何工作的答案,我正在脱发并且不确定问题是否只是 2 年前的解决方案并且情况已经改变.
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.
我如何安全地拦截自定义 Owin 中间件中的响应流 - 我的代码基于此,看起来它应该可以工作,但它没有
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 OnSendingHeaders 回调 - 读取响应正文 - 似乎是不同的 OWIN 版本,因为方法签名不起作用
OWIN OnSendingHeaders Callback - Reading Response Body - seems to be a different OWIN version, because method signature doesn't work
我想要做的是编写一个 OMC,可以检查来自 MVC 的响应流.
我所做的(在其他几次尝试中)是添加一个 OMC,将 context.Response.Body 设置为 MemoryStream,以便我可以倒带它并检查下游组件写入的内容:
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:
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;
.......
我发现 MemoryStream 总是空的,除非我从另一个 OMC 写入它. 所以看起来下游 OMC 正在使用我的 MemoryStream,但 MVC 响应不是,好像OWIN 管道在请求进入 MVC 之前完成,但这是不对的,是吗?
What I find is that the MemoryStream is always empty, unless I write to it from another 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?
完整代码:
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);
}
}
我还尝试了一个建议,将 response.Body 流设置为 Stream 子类,这样我就可以监视写入流的内容,并且奇怪地调用了 Stream.Write 方法,但它是空的字节数组,从来没有任何实际内容...
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...
推荐答案
MVC 不通过 OWIN 管道传递其请求.要捕获 MVC 响应,我们需要制作捕获响应数据的自定义响应过滤器
MVC does not pass its request through OWIN pipeline. To capture MVC response we need to make custom response filter that captures response data
/// <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);
}
}
这篇关于如何使用 OWIN 中间件组件检查 MVC 响应流?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持!