【问题标题】:Copying Http Request InputStream复制 Http 请求 InputStream
【发布时间】:2011-03-27 17:31:52
【问题描述】:

我正在实现一个代理操作方法,该方法转发传入的 Web 请求并将其转发到另一个网页,并添加一些标头。 action 方法适用于 GET 请求的文件,但我仍在努力转发传入的 POST 请求。

问题是我不知道如何正确地将请求正文写入传出的 HTTP 请求流。

以下是我目前所掌握内容的简化版本:

//the incoming request stream
var requestStream=HttpContext.Current.Request.InputStream;
//the outgoing web request
var webRequest = (HttpWebRequest)WebRequest.Create(url);
...

//copy incoming request body to outgoing request
if (requestStream != null && requestStream.Length>0)
            {
                long length = requestStream.Length;
                webRequest.ContentLength = length;
                requestStream.CopyTo(webRequest.GetRequestStream())                    
            }

//THE NEXT LINE THROWS A ProtocolViolationException
 using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
                {
                    ...
                }

当我对传出的 http 请求调用 GetResponse 时,我会收到以下异常:

ProtocolViolationException: You must write ContentLength bytes to the request stream before calling [Begin]GetResponse.

我不明白为什么会这样,因为 requestStream.CopyTo 应该负责写入正确数量的字节。

任何建议将不胜感激。

谢谢,

阿德里安

【问题讨论】:

  • @James Manning:感谢您的链接,但我已经完成了。我的代理适用于各种 GET 请求。只是 POST 请求正文仍然给我带来问题。
  • 在继续调用 webRequest.GetResponse() 之前,您是否尝试过在 webRequest.GetRequestStream() 返回的流上调用 Stream.Flush()?
  • @Mattias S:我刚做了,但似乎没有任何区别。
  • 出于调试的目的,我可能会将其更改为将流写入中间字节数组(memorystream,然后是 toarray),检查其内容和长度,然后写入字节数组。另外,恕我直言,您应该使用 using 将 webRequest.GetRequestStream() 分配给本地 var,因此您在写入之前关闭请求流,因此类似于 using (var rs = webRequest.GetRequestStream()) { requestStream.CopyTo( rs); } (或字节数组,如果你走那条路)。一旦我应该在我应该处理流的时候(并且刷新/关闭发生),我已经有很多错误消失了

标签: c# asp.net asp.net-mvc httpwebrequest webrequest


【解决方案1】:

尝试修改 if 语句中的块

long length = requestStream.Length;
webRequest.ContentLength = length;
requestStream.CopyTo(webRequest.GetRequestStream())

webRequest.Method = "POST";
webRequest.ContentLength = requestStream.Length;
webRequest.ContentType = "application/x-www-form-urlencoded";
Stream stream = webRequest.GetRequestStream();
requestStream.CopyTo(stream);
stream.Close();

【讨论】:

  • 我已经知道了(它在 ... 部分)。正如我所说,这只是缩短的版本。其他东西虽然很好,所以我认为它与这个问题无关。
【解决方案2】:

是的,.Net 对此非常挑剔。解决问题的方法是同时刷新关闭流。换句话说:

Stream webStream = null;

try
{
    //copy incoming request body to outgoing request
    if (requestStream != null && requestStream.Length>0)
    {
        long length = requestStream.Length;
        webRequest.ContentLength = length;
        webStream = webRequest.GetRequestStream();
        requestStream.CopyTo(webStream);
    }
}
finally
{
    if (null != webStream)
    {
        webStream.Flush();
        webStream.Close();    // might need additional exception handling here
    }
}

// No more ProtocolViolationException!
using (HttpWebResponse response = (HttpWebResponse)webRequest.GetResponse())
{
    ...
}

【讨论】:

  • 伟大的帖子布赖恩。奇迹般有效。但是,似乎 .CopyTo() 仅在 .NET 4.0+ 中受支持
  • 谢谢。这解决了我的问题。一个问题:您是否有理由在 finally 块中使用 Flush 和 Close,而不是将 webStream 包装在 using 块中? webRequest 有副作用吗?
  • 我很确定刷新不会改变任何东西...... GetRequestStream 返回一个 ConnectStream 的实例,并且这个类用一个空方法覆盖了 Flush,所以在它上面调用 Flush 根本没有任何效果。
【解决方案3】:

答案@brian 有效,但是,我发现一旦调用 requestStream.CopyTo(stream),它就会触发我的 HttpWebResponse。这是一个问题,因为我还没有准备好发送请求。因此,如果有人对并非所有请求标头或其他数据发送有问题,那是因为 CopyTo 正在触发您的请求。

【讨论】:

    猜你喜欢
    • 2010-12-25
    • 2018-04-24
    • 1970-01-01
    • 2014-06-29
    • 2014-08-19
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多