在HttpWebRequest完成流式传输大文件之前,Web API发布了

时间:2014-06-10 23:45:35

标签: asp.net-web-api

在我们的应用程序(Silverlight 5浏览器外客户端命中WebApi服务器)中,我们经常使用HttpClient发布/获取/删除等等客户端和服务器之间的所有实体。这一切在大多数情况下都能很好地工作,但是最近我们在上传(发布)更大的实体(> 30 / 35mb)时遇到了一个问题:我们开始流式处理并且在完成之前我们在Web API上的Post方法是点击,接收一个空实体。

我们无法理解发生了什么,并怀疑必须存在一些时间问题,因为这一切都取决于上传的大小。

为了进一步解释,我们的客户总结如下:

HttpResponseMessage response = await _client.SendAsync(request);
string jsonResult = await response.Content.ReadAsStringAsync();

...其中_client是我们的HttpClient并请求我们的HttpRequestMessage。如果它也是相关的(我试图不用代码:)来填充问题,请求中的内容是这样创建的:

request.Content = new StringContent(JsonConvert.SerializeObject(content), Encoding.UTF8, "application/json");

好吧,当我们调试这个时,我们的服务器上的Post方法在await _client.SendAsync(request)完成之前命中,这种“解释”为什么它在这种情况下接收一个空实体(更大的实体),其中当它工作时,等待呼叫结束,然后命中了。

如果由于HttpClient的某些限制(关于对AllowWriteStreamBuffering的访问)会给它带来更多的光,我们还测试了一个等效的场景,但直接使用了HttpWebRequest ......不幸的是,行为完全相同。这是相关的摘录:

httpRequest.BeginGetRequestStream(RequestStreamCallback, httpRequest);

(其中httpRequest是我们的HttpWebRequest,AllowWriteStreamBuffering = false),处理请求流的回调如下:

private void RequestStreamCallback(IAsyncResult ar)
{
   var request = ar.AsyncState as System.Net.HttpWebRequest;
   if (request != null)
   {
      var requestStream = request.EndGetRequestStream(ar);
      var streamWriter = new StreamWriter(requestStream) {AutoFlush = true};
      streamWriter.Write(_jsonContent);
      streamWriter.Close();
      requestStream.Close(); // Belt and suspenders... shouldn't be needed

      // Make async call for response
      request.BeginGetResponse(ResponseCallback, request);
   }
}

同样,对于较大的实体,当我们调试Web API上的Post方法时(在使用null参数的情况下)在streamWriter.Write完成并且命中了streamWriter.Close之前。

我们已经阅读了整个地方,并且现在已经和它斗争了好几天。任何帮助将不胜感激!

1 个答案:

答案 0 :(得分:0)

如果有人遇到这种情况,我终于弄清楚发生了什么。

本质上,Web API Post方法中的模型绑定机制在反序列化JSON时抛出异常,但异常有点“隐藏”......至少如果你不知道关于Web API的内部工作原理,就像我的情况一样。

My Post方法最初缺少此验证检查:

var errors = "";
if (!ModelState.IsValid)
{
  foreach (var prop in ModelState.Values)
  {
    foreach (var modelError in prop.Errors.Where(modelError => modelError != null))
    {
     if (modelError.Exception != null)
     {
       errors += "Exception message: " + modelError.Exception.Message + Environment.NewLine;
       errors += "Exception strack trace: " + modelError.Exception.StackTrace + Environment.NewLine;
     }
     else
       errors += modelError.ErrorMessage + Environment.NewLine;

     errors += " --------------------- " + Environment.NewLine + Environment.NewLine;
   }
 }

 return Request.CreateErrorResponse(HttpStatusCode.NoContent, errors);
}

这是一个"样本"检查,主要的想法是验证ModelState的有效性...在我们的破坏场景中是不有效的,因为Web API无法绑定实体,原因可以在ModelState.Values的错误属性。如上所述,帖子被击中了,但是有一个空实体。

顺便说一下,问题主要是因为我们并没有真正流式传输内容,而是使用了一个试图完全反序列化的StringContent ......但这是另一个故事,我们主要关注的是不了解什么是破坏和在哪里。

希望这有帮助。