前言
今天,在對接一個第三方平臺開放接口時遇到一個很棘手的問題,根據接口文檔組裝好報文,使用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之美
博客園首發:https://www.cnblogs.com/springhgui/p/16499439.html
最終解決前言中的問題,僅僅需要添加一行代碼
msg.Headers.ExpectContinue?=?false;
ExpectContinues屬性文檔:
至此問題解決,控制變量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
超時~
以上純屬個人理解,有不正確之處,還請指正~