【问题标题】:Why can't I read Http Request Input stream twice?为什么我不能两次读取 Http Request Input 流?
【发布时间】:2014-03-25 03:41:30
【问题描述】:

我正在输入一些调试代码来测试一些东西,然后调试代码没有按预期运行。下面的示例是演示我的问题的简化代码。

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

为什么我不能第二次回溯并读取 InputStream?在下面的示例中,body2 始终为空。在第二组中,CanSeek 仍然为真,第二次调用 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);
    }
}

【问题讨论】:

  • CanSeek 是否返回 true?您目前正在有条件地寻找,但随后假设寻找已经奏效。你为什么还要这样做?我个人期望至少对于大型请求主体,流可能不会在内存中缓冲。如果您想创建副本,我建议您首先然后从您的副本中多次阅读。将 Stream.CopyTo 与 MemoryStream 结合使用。
  • 是的,CanSeek 确实返回 true 并执行 ReadToEnd。好点子。我将更新问题以澄清。
  • 你试过 Fiddler 吗?
  • 你可以像 Jon 指出的那样将流复制到 MemoryStream,但是 Web 请求中的 Stream 接口实际上只是不支持 all的 TCP 流的抽象> 到您可以在该套接字上执行的可能的 TCP 操作(即,您可以请求再次发送数据包——但如果您只是想重新读取数据,您很少想要这样做) .
  • @JonSkeet - 所有的好建议,谢谢。

标签: c# .net asp.net-mvc asp.net-web-api


【解决方案1】:

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

一旦您读取位置,就会进入最后一个值,然后从那里尝试第二次读取。 因此,在阅读之前,请将位置设置为零。

希望对你有帮助。

【讨论】:

  • 知道如何在没有HttpContext.Current的自托管场景中执行此操作吗?
  • 这个不行,因为using语句,流是空的
  • 去掉 using 就可以了。
【解决方案2】:

StreamReader 调用Dispose 时在给定的流上处置。要使流保持打开状态,请使用 appropriate constructor 代替 StreamReader。 或者更好的是,只需将其复制到缓冲区即可。来自 MSDN:

从流中读取时,使用缓冲区更有效 与流的内部缓冲区大小相同。

例如,参见this question

【讨论】:

  • 谜团解开了。谢谢你。显然,带有 leaveOpen 参数的 StreamReader 的构造函数仅在 .net 4.5 中可用,而我使用的是 4.0。我将 Using 语句解包为 try/catch/finally,然后我无法关闭第一个 StreamReader,果然,第二遍有效。这只是我为了调试其他东西而放入的一些丢弃代码,但这种神秘的行为让我很困惑。谢谢你的解释。
  • 并且只是从上面的这个答案和 cmets 中总结出最好的信息:如果你想读取流两次,不要直接对底层流使用 StreamReader,因为当 StreamReader 关闭时,它关闭底层流。首先制作一个 MemoryStream 副本,然后从中读取。
  • stream.Seek(0, SeekOrigin.Begin);
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-10-25
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2016-05-02
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多