6

嗨,我想将一些动态生成的内容上传到我的 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 读取或写入操作。)。

为什么我会在这里出现异常。有时第一次写入有效,而后期写入会引发异常。在开始时 stream.CanWrite 属性为真,但在第一次或第二次或第三次写入后它变为假......然后在下一次写入时抛出异常。

编辑:更改 AllowWriteStreamBuffering 没有帮助

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

  • GetRequestStream(将异步写入流)(在第一次写入后将请求发送到服务器)然后:
  • 获取响应异步()
  • 获取响应流()

我认为“GetResponseAsync”会触发客户端发送请求(目前仅限标头)。但这不是必需的,因为在我将第一位写入流之后,请求已经发送。

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

4

2 回答 2

9

我发现了我的问题。

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

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

  • GetRequestStream(将异步写入流)(在第一次写入后将请求发送到服务器)然后:
  • 获取响应异步()
  • 获取响应流()

我的理解是“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();
于 2014-08-29T12:15:02.510 回答
0

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

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

于 2014-08-29T10:22:22.400 回答