我正在重新实现一个请求记录器作为Owin Middleware,它记录了所有传入请求的请求URL和正文。 我能够读取正文,但如果我在控制器中执行body参数为null。
我猜它是空的,因为流位置在末尾,因此在尝试反序列化主体时没有任何东西可以读取。我在以前版本的Web API中遇到了类似的问题,但是能够将Stream位置设置回0.这个特定的流会引发This stream does not support seek operations
异常。
在最新版本的Web API 2.0中,我可以在我的请求记录器中调用Request.HttpContent.ReadAsStringAsync()
,并且正文仍然可以直接到达控制器。
如何在阅读后回放信息流?
或
如何在不消费的情况下阅读请求正文?
public class RequestLoggerMiddleware : OwinMiddleware
{
public RequestLoggerMiddleware(OwinMiddleware next)
: base(next)
{
}
public override Task Invoke(IOwinContext context)
{
return Task.Run(() => {
string body = new StreamReader(context.Request.Body).ReadToEnd();
// log body
context.Request.Body.Position = 0; // cannot set stream position back to 0
Console.WriteLine(context.Request.Body.CanSeek); // prints false
this.Next.Invoke(context);
});
}
}
public class SampleController : ApiController
{
public void Post(ModelClass body)
{
// body is now null if the middleware reads it
}
}
答案 0 :(得分:37)
刚刚找到一个解决方案。使用包含数据的新流替换原始流。
public override Task Invoke(IOwinContext context)
{
return Task.Run(() => {
string body = new StreamReader(context.Request.Body).ReadToEnd();
// log body
byte[] requestData = Encoding.UTF8.GetBytes(body);
context.Request.Body = new MemoryStream(requestData);
this.Next.Invoke(context);
});
}
如果您正在处理大量数据,我确信FileStream
也可以替代。
答案 1 :(得分:10)
这里是Despertar对第一个答案的一个小改进,这对我有很大帮助,但在处理二进制数据时遇到了一个问题。将流提取到的中间步骤一个字符串然后使用Encoding.UTF8.GetBytes(body)
将其放回到字节数组中会混淆二进制内容(除非是UTF8编码的字符串,否则内容将会更改)。这是我使用Stream.CopyTo()
:
public override async Task Invoke(IOwinContext context)
{
// read out body (wait for all bytes)
using (var streamCopy = new MemoryStream())
{
context.Request.Body.CopyTo(streamCopy);
streamCopy.Position = 0; // rewind
string body = new StreamReader(streamCopy).ReadToEnd();
// log body
streamCopy.Position = 0; // rewind again
context.Request.Body = streamCopy; // put back in place for downstream handlers
await this.Next.Invoke(context);
}
}
此外,MemoryStream
很不错,因为您可以在记录完整正文之前检查流的长度(如果有人上传大文件,这是我不想做的事情)。
答案 2 :(得分:-3)
我知道这是旧的,但只是为了帮助遇到这种情况的人。
您需要在流媒体上寻找:context.Request.Body.Seek(0, SeekOrigin.Begin);