2014-02-23 72 views
10

我正在加入一些调试代码来测试一些事情,然后调试代码没有按预期运行。下面的示例是演示我的问题的简化代码。为什么我无法读取Http请求输入流两次?

这是在.NET 4中,并使用WebApi,我试图在调试代码中打印出http请求的正文。为此,我寻找输入流并读取流。它第一次运行良好,但如果我尝试再次阅读它,我会得到一个空字符串。

为什么我不能重新寻找并再次读取InputStream?在下面的例子中,body2总是空的。在第二组中,CanSeek仍然是true,第二次调用ReadToEnd()会返回一个空字符串,覆盖默认值。

using System.IO; 
using System.Net; 
using System.Net.Http; 
using System.Web; 
using System.Web.Http; 

public class TestController : ApiController 
{ 

    public class TestOutuput 
    { 
     public string firstRead; 
     public string secondRead; 
    } 

    public HttpResponseMessage Post() 
    { 
     string body1 = "default for one"; 
     string body2 = "default for two"; 
     if (HttpContext.Current.Request.InputStream.CanSeek) 
     { 
      HttpContext.Current.Request.InputStream.Seek(0, System.IO.SeekOrigin.Begin); 
     } 
     using (var reader = new StreamReader(HttpContext.Current.Request.InputStream)) 
     { 
      body1 = reader.ReadToEnd(); 
     } 

     if (HttpContext.Current.Request.InputStream.CanSeek) 
     { 
      HttpContext.Current.Request.InputStream.Seek(0, System.IO.SeekOrigin.Begin); 
     } 
     using (var reader2 = new StreamReader(HttpContext.Current.Request.InputStream)) 
     { 
      // this is always empty, even after seek back to origin 
      body2 = reader2.ReadToEnd(); 
     } 

     TestOutuput testOutput = new TestOutuput() { firstRead = body1, secondRead = body2 }; 
     HttpResponseMessage response = new HttpResponseMessage(); 
     return Request.CreateResponse<TestOutuput>(HttpStatusCode.OK, testOutput); 
    } 
} 
+5

那么'CanSeek'返回'true'?您目前*有条件*寻求,但后来假设寻求已经奏效。你为什么这么做?我个人认为*至少对于大型请求体来说,这个流可能不会被缓存在内存中。如果你想创建一个副本,我建议你先做*先*,然后从副本中多次读取。将Stream.CopyTo与MemoryStream结合使用。 –

+0

是的,CanSeek返回true并执行ReadToEnd。好点子。我会更新这个问题来澄清。 –

+0

你尝试过提琴手吗? –

回答

15

StreamReader在处置给定流时调用Dispose。要让流打开,请使用appropriate constructor作为StreamReader。或者更好,只需将其复制到缓冲区即可。来自MSDN:

从Stream读取时,使用缓冲区效率更高,该缓冲区的大小与流的内部缓冲区大小相同。

例如参见this question

+1

神秘解决了。谢谢。显然,具有leaveOpen参数的StreamReader的构造函数只能在.net 4.5中使用,而我正在使用4.0。我将Using语句解包到try/catch/finally中,然后我无法关闭第一个StreamReader,果然,第二遍工作。这只是一些扔掉我调试其他东西的代码,但神秘的行为让我难堪。感谢您的解释。 –

+1

只是从上面的回答和注释中总结出最好的信息:如果你想读两次流,不要直接使用StreamReader来处理基础流,因为当StreamReader关闭时,它会关闭基础流。首先制作一个MemoryStream拷贝,然后从中读取。 –

+0

stream.Seek(0,SeekOrigin.Begin); – Taran

5

HttpContext.Current.Request.InputStream.Position=0;

一旦你阅读的位置进入最后一个值,从那里它想读第二遍。 因此,在您阅读之前,将位置设置为零。

希望它有帮助。

+0

有关如何在没有'HttpContext.Current'的自主托管方案中执行此操作的任何想法? – peco