发布JSON数据WebAPI

时间:2017-01-20 19:26:53

标签: c# json azure asp.net-web-api

将数据发布到Azure中托管的Web API服务时,我遇到了一个奇怪的问题,并且非常感谢。

我编写了一个非常基本的Web API服务,它有一个控制器,通过Post方法接收List。我使用从HttpClientFactory创建的客户端通过控制台应用程序将数据发布到此API。

当我在本地调试时,一切正常,我成功发布了一个包含100个元素的List,并将其写入我的本地数据库。如果我将本地Web API代码切换为指向我的Azure数据库,它仍然可以正常工作,我可以毫无问题地收到100个元素并写入Azure数据库实例。

当我将API服务部署到我的Azure帐户并挂钩我的控制台应用程序以向其发布数据时 - 它与包含最多45个元素的List完美配合,并且PostAsync等待大约6分钟然后失败内部服务器错误。

所以我的问题是为什么服务能够在大约一秒钟内发布43个元素到44个元素失败,但需要花费6分钟才能完成?

我应该指出我的List包含100个重复的项目,因为我正在生成用于测试的数据,因此集合中的特定元素没有任何错误(或不同)。

T是一个简单的类,有大约30个属性,没有子元素,所以我没有发布一个庞大的结构。

我的想法是,这必须是一个大小限制,在我的本地计算机上没有生效,但是当我部署到Azure时。我绝不是Web API专家,所以我不知道从哪里开始寻找。

一些客户端代码(当i <= 43时完美运行):

List<Foo> results = new List<Foo>();

for (int i = 1; i < 100; i++)
{
    results.Add(new Foo() { // Populated with data - removed for brevity });
}

StringContent content = new StringContent(JsonConvert.SerializeObject(results), Encoding.UTF8, "application/json");
HttpResponseMessage postDataResponse = await postClient.PostAsync(myWebApiServiceURL, content);

控制器方法:

[HttpPost]
public List<Foo>Post([FromBody]List<Foo> value)
{
   return value;
}

我试过的Web.config设置没有效果:

<httpRuntime targetFramework="4.6.1"  maxRequestLength="200000000" requestLengthDiskThreshold="16384" />

<add key="aspnet:MaxJsonDeserializerMembers" value="20000000"/>

我刚刚进行了一次测试,它不能处理44个元素内容长度= 53278

它的最大长度是当我的总元素是43内容长度= 48361时。

注意:我刚刚运行了一些测试,如果我减少了一些字符串属性中的数据长度(从而减少了每个元素的占用空间),我可以发布更多元素。因此,它必须是某个地方的消息大小限制!!

  

更新 - HMAC身份验证

确定原因始终是您在问题中未记录的最不期望的事情。我在我的API上启用了HMAC身份验证,当我从控制器中删除属性时,我可以发布10000个元素而不会出现问题。一旦我启用它,我就会被限制回到43。

在客户端上,我正在生成请求内容的MD5哈希作为标头签名的一部分,此过程在Web API服务的HMAC属性中重复。如果我从我的客户端和服务中删除这部分签名,那么我可以发布尽可能多的数据。我在客户端上注释掉的代码如下所示:

//Checking if the request contains body, usually will be null with HTTP GET and DELETE
if (request.Content != null)
{
   byte[] content = await request.Content.ReadAsByteArrayAsync();
   MD5 md5 = MD5.Create();
   //Hashing the request body, any change in request body will result in different hash, we'll incure message integrity
   byte[] requestContentHash = md5.ComputeHash(content);
   requestContentBase64String = Convert.ToBase64String(requestContentHash);
}

我在服务中注释的代码如下:

byte[] hash = await ComputeHash(req.Content);

if (hash != null)
{
   requestContentBase64String = Convert.ToBase64String(hash);
}

ComputeHash功能:

private static async Task<byte[]> ComputeHash(HttpContent httpContent)
{
   using (MD5 md5 = MD5.Create())
   {
      byte[] hash = null;
      var content = await httpContent.ReadAsByteArrayAsync();
      if (content.Length != 0)
      {
        hash = md5.ComputeHash(content);
      }
        return hash;
   }
}

因此,上面的代码注释掉requestContentBase64String在客户端和服务器上始终是一个空字符串,因此内容的MD5哈希不会被用作身份验证签名的一部分。

我现在必须研究这种行为的根本原因。

1 个答案:

答案 0 :(得分:0)

https://blogs.msdn.microsoft.com/azureossds/2016/06/15/uploading-large-files-to-azure-web-apps/

检查Azure的最大帖子大小。取决于您的配置可能是问题。更改配置或查看将邮件大小减小为更小的块。