HttpWebRequest分块/异步POST

本文关键字:POST 异步 分块 HttpWebRequest | 更新日期: 2023-09-27 18:19:56

嗨,我想上传一些动态生成的内容到我的web api。在客户端上,我使用HttpWebRequest。数据应该同步上传,我想在(!)我执行HTTP请求后写入流。

(从服务器到客户端,它确实运行良好,但从客户端到服务器,我会遇到一些例外)。

客户端实现看起来像:

HttpWebRequest httpWebRequest = HttpWebRequest.Create(myUrl) as HttpWebRequest;
httpWebRequest.Method = "POST";
httpWebRequest.Headers["Authorization"] = "Basic " + ... ;
httpWebRequest.PreAuthenticate = true;
httpWebRequest.SendChunked = true;
//httpWebRequest.AllowWriteStreamBuffering = false; //does not help...
httpWebRequest.ContentType = "application/octet-stream";
Stream st = httpWebRequest.GetRequestStream();
Task<WebResponse> response = httpWebRequest.GetResponseAsync();
// NOW: Write after GetResponse()
var b = Encoding.UTF8.GetBytes("Test1");
st.Write(b, 0, b.Length);
b = Encoding.UTF8.GetBytes("Test2");
st.Write(b, 0, b.Length);
b = Encoding.UTF8.GetBytes("Test3");
st.Write(b, 0, b.Length);
st.Close();
var x = response.Result;
Stream resultStream = x.GetResponseStream();
//do some output...

我在stream.write().上得到异常(NotSupportedException:流不支持并发IO读或写操作。)

为什么我在这里得到一个例外。有些时候,第一次写入有效,而最后一次写入则抛出异常。在溪流的起点。CanWrite属性为true,但在第一次、第二次或第三次写入后,它变为false。。。然后在下一次写入时抛出异常。

编辑:更改AllowWriteStreamBuffering对没有帮助

附录:我发现了我的问题这个问题是由我的代码顺序引起的。我必须按以下顺序命名:

  • GetRequestStream(将异步写入流)(请求在第一次写入后发送到服务器)那么:
  • GetResponseAsync()
  • GetResponseStream()

我认为"GetResponseAsync"会触发客户端发送请求(目前仅限于标头)。但这是不必要的,因为在我将第一个比特写入流之后,请求已经被发送了。

我问题的第二个原因:小提琴手。(Fiddler目前只支持响应流,不支持请求流)

HttpWebRequest分块/异步POST

我发现了我的问题

我的代码顺序导致了问题。

解决方案是按以下顺序调用:

  • GetRequestStream(向流写入异步)(第一次写入后将请求发送到服务器),然后:
  • GetResponseAsync()
  • GetResponseStream()

我的理解是,"GetResponseAsync"会触发客户端发送请求(目前仅限于标头),但我发现这是一个不可接受的步骤,因为请求是在前几个位写入流后发送的。

我的问题的第二个原因是Fiddler,但Fiddler只支持流式响应,而不支持请求。

获得引用的代码"HttpWebRequest"类:

HttpWebRequest httpWebRequest = HttpWebRequest.Create("http://xxx") as HttpWebRequest;
httpWebRequest.Method = "POST";
httpWebRequest.Headers["Authorization"] = "Basic " + Convert.ToBase64String(Encoding.ASCII.GetBytes("user:pw"));
httpWebRequest.PreAuthenticate = true;    
httpWebRequest.SendChunked = true;
httpWebRequest.AllowWriteStreamBuffering = false;
httpWebRequest.AllowReadStreamBuffering = false;    
httpWebRequest.ContentType = "application/octet-stream";
Stream st = httpWebRequest.GetRequestStream();
Console.WriteLine("Go");
try
{
    st.Write(buffer, 0, buffer.Length); //with the first write, the request will be send.
    st.Write(buffer, 0, buffer.Length);
    st.Write(buffer, 0, buffer.Length);
    for (int i = 1; i <= 10; i++)
    {        
        st.Write(buffer, 0, buffer.Length); //still writing while I can read on the stream at my ASP.NET web api
    }
}
catch (WebException ex)
{
    var y = ex.Response;
}
finally
{
    st.Close();
}
// Now we can read the response from the server in chunks
Task<WebResponse> response = httpWebRequest.GetResponseAsync();
Stream resultStream = response.Result.GetResponseStream();
byte[] data = new byte[1028];
int bytesRead;
while ((bytesRead = resultStream.Read(data, 0, data.Length)) > 0)
{
    string output = System.Text.Encoding.UTF8.GetString(data, 0, bytesRead);
    Console.WriteLine(output);
}

代码获得引用,"HttpClient"类:

HttpClientHandler ch = new HttpClientHandler();
HttpClient c = new HttpClient(ch);    
c.DefaultRequestHeaders.TransferEncodingChunked = true;    
c.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Basic", Convert.ToBase64String(Encoding.ASCII.GetBytes("user:pw")));
Stream stream = new MemoryStream();
AsyncStream asyncStream = new AsyncStream(); // Custom implementation of the PushStreamContent with the method, "WriteToStream()".
PushStreamContent streamContent = new PushStreamContent(asyncStream.WriteToStream);
HttpRequestMessage requestMessage = new HttpRequestMessage(new HttpMethod("POST"), "http://XXX") { Content = streamContent };
requestMessage.Headers.TransferEncodingChunked = true;
HttpResponseMessage response = await c.SendAsync(requestMessage, HttpCompletionOption.ResponseHeadersRead);
// The request has been sent, since the first write in the "WriteToStream()" method.
response.EnsureSuccessStatusCode();
Task<Stream> result = response.Content.ReadAsStreamAsync();
byte[] data = new byte[1028];
int bytesRead;
while ((bytesRead = await result.Result.ReadAsync(data, 0, data.Length)) > 0)
{
    string output = System.Text.Encoding.UTF8.GetString(data, 0, bytesRead);
    Console.WriteLine(output);
}
Console.ReadKey();

您正在多个线程上同时使用HttpWebRequest的对象。response是一个与写入同时运行的任务。这显然是线程不安全。

此外,我看不出你想要实现什么。HTTP有一个先请求后响应的模型。在接收到整个请求之前,服务器(通常)不能发送单个响应字节。从理论上讲,这是可能的。但这是非常不寻常的,并且可能不受.NET BCL的支持。这只能由您的(非常不寻常的)自定义HTTP服务器支持。