简体   繁体   English

HttpWebRequest chunked / async POST

[英]HttpWebRequest chunked/async POST

Hi I want to upload some dynamic generated content to my web api. 嗨,我想将一些动态生成的内容上传到我的网络API。 On the client I use the HttpWebRequest. 在客户端我使用HttpWebRequest。 The data should be uploaded sync and I want to write to the stream AFTER(!) I executed the HTTP-request. 数据应该上传同步,我想写入流AFTER(!)我执行了HTTP请求。

(From server to client it does work fine, but from client to server i get some exceptions). (从服务器到客户端它确实工作正常,但从客户端到服务器我得到一些例外)。

The client implementation looks like: 客户端实现如下:

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...

I get exceptions (NotSupportedException: The stream does not support concurrent IO read or write operations.) on stream.write(). 我在stream.write()上得到异常(NotSupportedException:流不支持并发IO读或写操作。)。

Why do I get here an exceptions. 为什么我这里有例外。 Some times the first writes worke and the late writes throw the exception. 有时第一次写入worke而后期写入会抛出异常。 On the beginning the stream.CanWrite property is true, but after the first or second or thrird write it goes false... And then the exception is thrown on the next write. 在开始时,stream.CanWrite属性为true,但在第一次或第二次或第四次写入后,它变为false ...然后在下一次写入时抛出异常。

Edit: Changing AllowWriteStreamBuffering did not help 编辑:更改AllowWriteStreamBuffering没有帮助

Appendix: I found my problem. 附录: 我发现了我的问题。 This problem is caused by the order of my code. 此问题是由我的代码的顺序引起的。 I have to call it in this order: 我必须按此顺序调用它:

  • GetRequestStream (writing async to the stream) (the request is send to the server after the first write) then: GetRequestStream(将异步写入流)(请求在第一次写入后发送到服务器)然后:
  • GetResponseAsync() GetResponseAsync()
  • GetResponseStream() 的GetResponseStream()

I thought "GetResponseAsync" triggers the client to send the Request (for now the headers only). 我认为“GetResponseAsync”会触发客户端发送请求(现在只有标头)。 But it is not necessary because the request is already send after I write the first bits to the stream. 但是没有必要,因为在我将第一个比特写入流后,请求已经发送。

Second cause of my problems: Fiddler. 我问题的第二个原因:提琴手。 (Fiddler currently only supports streaming of responses and not requests) (Fiddler目前仅支持响应流而不是请求)

I found my problem. 我发现了我的问题。

The order of my code caused the problem. 我的代码顺序导致了问题。

The solution is to call it in this order: 解决方案是按此顺序调用它:

  • GetRequestStream (writing async to the stream) (the request is send to the server after the first write) then: GetRequestStream(将异步写入流)(请求在第一次写入后发送到服务器)然后:
  • GetResponseAsync() GetResponseAsync()
  • GetResponseStream() 的GetResponseStream()

My understanding is that "GetResponseAsync" triggers the client to send the request (for now the headers only), but I have discovered it to be an uneccesary step, because the request had already been sent after the first few bits had been written to the stream. 我的理解是“GetResponseAsync”触发客户端发送请求(现在只是头文件),但我发现它是一个不成功的步骤,因为请求已经在最初的几位写入之后发送了流。

The second cause of my problems is Fiddler, but Fiddler only supports streaming of responses, and not requests. 我的问题的第二个原因是Fiddler,但Fiddler只支持响应流,而不支持请求。

The code acheived referencing, 'HttpWebRequest' class: 该代码实现了引用,'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);

}

The code acheived referencing, 'HttpClient' class: 该代码实现了引用,'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();

You are using objects of an HttpWebRequest on multiple threads concurrently. 您正在同时在多个线程上使用HttpWebRequest对象。 response is a task that runs concurrently with your writes. response是与您的写入同时运行的任务。 That is clearly thread-unsafe. 这显然是线程不安全的。

Also, I don't see what you want to achieve. 另外,我看不到你想要达到的目标。 HTTP has a request-then-response model. HTTP具有请求然后响应模型。 The server can't (usually) send a single response byte before receiving the entire request. 在接收整个请求之前,服务器不能(通常)发送单个响应字节。 Theoretically, this is possible. 从理论上讲,这是可能的。 But that is very unusual and likely not supported by the .NET BCL. 但这很不寻常,可能不受.NET BCL的支持。 This would only be supported by a (very unusual) custom HTTP server of yours. 这只能由您的(非常不寻常的)自定义HTTP服务器支持。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM