2014-02-15 48 views
24

我正在重新实现一个请求记录器作为Owin中间件,它记录了所有传入请求的请求url和正文。 我能够阅读身体,但如果我在我的控制器中的身体参数为空。倒带请求正文流

我猜它是空的,因为流的位置是在最后,所以当它试图反序列化身体时没有什么可以阅读的。在以前版本的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 
    } 
} 

回答

29

刚刚找到一个解决办法。用包含数据的新流替换原始流。

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也将工作作为替代品。

+4

另一种替代方法: 流anotherStream =新的MemoryStream(); context.Request.Body.CopyToAsync(anotherStream); –

+9

在ASP.NET Core(其中invoke方法将具有以下签名'异步任务调用(HttpContext上下文)'),您可以执行以下操作以获取缓冲流,以便您可以多次查找和读取它; 'context.Request.EnableRewind()'(在Microsoft.AspNetCore.Http.Internal.BufferingHelper中找到的HttpRequest扩展方法)。 – jfiskvik

+0

@jfiskvik您的意见对我来说是非常有用的,它帮助我一些处理,在我的中间件 – ExtremeSwat

-3

我知道这是旧的,但只是为了帮助那些遇到这个人。 你需要寻求流:context.Request.Body.Seek(0, SeekOrigin.Begin);

+4

同样的事情发生的'Body.Position = 0',这将引发与消息'NotSupportedException'后倒带我流'该流不支持寻求操作'。另外'Body.CanSeek'属性返回false。 – Despertar

4

这里有一个小的改进,以通过对神电的第一个答案,它帮了我很多,但二进制数据时,我遇到了一个问题。提取流成一个字符串,然后把它放回使用Encoding.UTF8.GetBytes(body)弄乱二进制内容(内容会改变,除非它是一个UTF8编码的字符串)的字节数组的中间步骤。下面是使用Stream.CopyTo()我的解决办法:

public override Task Invoke(IOwinContext context) 
{ 
    return Task.Run(() => 
    { 
     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 

      this.Next.Invoke(context); 
     } 
    }); 
} 

此外,MemoryStream是好的,因为你可以记录完整的身体(这是我不希望的情况下,有人做上传一个巨大的文件)之前检查数据流的长度。