乐趣区

关于后端:HTTP协议之Expect爬坑

前言

明天,在对接一个第三方平台凋谢接口时遇到一个很辣手的问题,依据接口文档组装好报文,应用 HttpClient 发动 POST 申请时始终超时,对方服务器始终不给任何响应。

发动申请的代码如下:

using (var httpClient = new HttpClient())
{var msg = new HttpRequestMessage()
    {Content = new StringContent(postJson, Encoding.UTF8, "application/json"),
        Method = HttpMethod.Post,
        RequestUri = new Uri(apiUrl),
    };
    
    // 这里会始终阻塞,直到超时
    var res =  httpClient.SendAsync(msg).ConfigureAwait(false).GetAwaiter().GetResult();

    if (res.StatusCode != HttpStatusCode.OK)
    {throw new Exception(res.StatusCode.ToString());
    }

    return res.Content.ReadAsStringAsync().ConfigureAwait(false).GetAwaiter().GetResult();
}

异步申请超时勾销谬误如下:

这种状况首先狐疑对方服务是不是有问题
然而通过确认,对方服务没问题,并且应用将申请的 url报文 粘贴到 PostMan 进行申请,迅速失去返回报文,一切正常。

排除了对方服务的问题,那是咱们的代码问题?
可是下面 HttpClient 发动 Post 申请的代码写了不晓得多少遍,始终都没问题,明天怎么就不行了呢,我敢保障这么写没故障。

遇到这种状况该如何解决呢?

爬坑过程

遇到这种问题,相比大部分人开始各种参数换来换去,各种库换来换去,可能最终蒙成了。然而这里我置信 PostMan 能够申请胜利,弱小的 HttpClient 肯定能够,肯定是是哪个参数问题,有教训的新手首先就会想到: 接口的协定中是不是对 Header 有什么特地的要求,这里查问文档,没有什么特地要求。

控制变量法

既然咱们不晓得为什么,也猜不到,那就 控制变量法 去解决。这里能想到的就是抓包,抓取 PostMan 胜利的申请报文以及咱们失败的报文,比照差别。

抓包工具应用的是Fiddler

Postman 报文

POST http://xxx.xxx.xxx.xxx:30000/parking/carin/V1 HTTP/1.1
Content-Type: application/json
User-Agent: PostmanRuntime/7.29.2
Accept: */*
Postman-Token: 14547b64-d8f6-4b0b-9fa9-48c9ec74a8f6
Host: xxx.xxx.xxx.xxx:30000
Accept-Encoding: gzip, deflate, br
Connection: keep-alive
Content-Length: 563

{"data": ... 这里省略了具体 json 内容}

HttpClient 报文

POST http://118.31.110.35:30000/parking/carin/V1 HTTP/1.1
Content-Type: application/json; charset=utf-8
Host: 118.31.110.35:30000
Content-Length: 563
Expect: 100-continue
Connection: Keep-Alive

{"data": ... 这里省略了具体 json 内容}

差别排查

  1. 因为 body 中的内容是一样的,这里就不必比照了。
  2. 两个申请的 Header 存在差别,那咱们就将差别一个一个抹平。
  3. Content-TypeHttpClient 中多了 charset=utf-8,这个应该不影响,http 协定默认就是 utf8。
  4. User-AgentHttpClient 中没有,那咱们加上截然不同的User-Agent,测试,仍旧超时。
  5. AcceptHttpClient 中没有,抹平,测试,仍旧超时。
  6. Postman-TokenHttpClient 中没有,抹平,测试,仍旧超时。
  7. Accept-EncodingHttpClient 中没有,抹平,测试,仍旧超时。

到这里 Postman 中有的,咱们 HttpClient 中都有了,居然还超时,这里尽管曾经保障大部分参数都一样了,然而控制变量法要求所有参数都一样,这里还没有保障,因为 HttpClient 多了一个 Expect 头,咱们还没保障统一。

  1. HttpClient的申请头中 Expect: 100-continuePostman报文中不存在,去掉Expect,测试,胜利了!!
  2. 那咱们锁定 Expect: 100-continue 导致了咱们的申请无响应,还原之前所有的抹平操作,仅仅移除Expect: 100-continue,测试,仍然胜利。

本文为 Gui.H 原创文章,公布于公众号:dotnet 之美,转载注明出处

博客园首发:https://www.cnblogs.com/sprin…

最终解决前言中的问题,仅仅须要增加一行代码

msg.Headers.ExpectContinue = false;

ExpectContinues 属性文档:

至此问题解决,控制变量 yyds

Expect 是什么

参考 Expect 的定义
https://developer.mozilla.org…

Expect 是一个申请音讯头,蕴含一个冀望条件,示意服务器只有在满足此冀望条件的状况下能力妥善地解决申请。

Expect

标准中只规定了一个冀望条件,即 Expect: 100-continue, 对此服务器能够做出如下回应:

  • 100 如果音讯头中的冀望条件能够失去满足,使得申请能够顺利进行的话,
  • 417 (Expectation Failed) 如果服务器不能满足冀望条件的话;也能够是其余任意示意客户端谬误的状态码(4xx)。

例如,如果申请中 Content-Length 的值太大的话,可能会受到服务器的回绝。

Expect 有啥益处

让客户端在发送申请数据之前去判断服务器是否违心接管该数据,如果服务器违心接管,客户端才会真正发送数据,如果客户端间接发送申请数据,然而服务器又将该申请回绝的话,这种行为将带来很大的资源开销。

Expect 有啥坑

不是所有的服务器都会正确应答 100-continue, 比方 lighttpd, 就会返回 417 Expectation Failed。

超时的起因

HttpClient默认携带了 Expect 头,咱们申请带上了 Expect: 100-continue 的话是不会立即发送 body 中的报文给服务器,须要服务器须要对 Expect: 100-continue 做出响应,然而对方服务器不反对 Expect 当然不能做出响应,在前言说的问题中,也就是 HttpClient 在等对方服务器响应 Expect,而后再发送报文,而对方服务器看来,咱们怎么还不发送报文过去,单方都在等数据,最终HttpClient 超时~

以上纯属集体了解,有不正确之处,还请斧正~

本文由 mdnice 多平台公布

退出移动版