2014-02-15 51 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是好的,因爲你可以記錄完整的身體(這是我不希望的情況下,有人做上傳一個巨大的文件)之前檢查數據流的長度。