我正在尝试编写一个简单的OWIN中间件,以便拦截响应流。我要做的是用自定义的基于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)我可以记录写入响应流的字节数,并将其与预期响应长度相关联。然而,在使用分块传输编码的响应的情况下,长度是未知的。
b)当在响应流上调用Dispose
时,我可以确定响应流是完整的。但是,OWIN/Katana基础设施从不在替换的流上调用Dispose。
我一直在调查Opaque Streaming,以了解操纵底层HTTP协议是否可行,但我似乎没有发现Katana是否支持不透明流。
有没有办法实现我想要的东西?
发布于 2014-10-06 20:44:57
我认为您不需要子类流,但下面是如何读取响应的方法。只需确保此中间件是OWIN管道中的第一个中间件,这样它就是最后一个检查响应的中间件。
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
是特定于片断的。然而,这与您的问题无关。
https://stackoverflow.com/questions/26214113
复制相似问题