

HTTP协议之Expect爬坑 - gui.h
source link: https://www.cnblogs.com/springhgui/p/16499439.html
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

HTTP协议之Expect爬坑 - gui.h - 博客园
今天,在对接一个第三方平台开放接口时遇到一个很棘手的问题,根据接口文档组装好报文,使用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内容}
- 因为
body
中的内容是一样的,这里就不用对比了。 - 两个请求的Header存在差异,那我们就将差异一个一个抹平。
Content-Type
在HttpClient
中多了charset=utf-8,这个应该不影响,http协议默认就是utf8。User-Agent
在HttpClient
中没有,那我们加上一模一样的User-Agent
,测试,依旧超时。Accept
在HttpClient
中没有,抹平,测试,依旧超时。Postman-Token
在HttpClient
中没有,抹平,测试,依旧超时。Accept-Encoding
在HttpClient
中没有,抹平,测试,依旧超时。
到这里Postman中有的,我们HttpClient
中都有了,竟然还超时,这里虽然已经保证大部分参数都一样了,但是控制变量法要求所有参数都一样,这里还没有保证,因为HttpClient多了一个Expect头,我们还没保证一致。
HttpClient
的请求头中Expect: 100-continue
在Postman
报文中不存在,去掉Expect
,测试,成功了!!- 那我们锁定
Expect: 100-continue
导致了我们的请求无响应,还原之前所有的抹平操作,仅仅移除Expect: 100-continue
,测试,依然成功。
本文为Gui.H原创文章,发布于公众号:dotnet之美,转载注明出处
最终解决前言中的问题,仅仅需要添加一行代码
msg.Headers.ExpectContinue = false;
至此问题解决,控制变量yyds
Expect是什么
参考Expect
的定义
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect
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
超时~
以上纯属个人理解,有不正确之处,还请指正~
__EOF__
Recommend
About Joyk
Aggregate valuable and interesting links.
Joyk means Joy of geeK