今天,在對接一個第三方平臺開放介面時遇到一個很棘手的問題,根據介面檔案組裝好報文,使用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
中的內容是一樣的,這裡就不用對比了。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;
ExpectContinues屬性檔案:
至此問題解決,控制變數yyds
參考Expect
的定義
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Expect
Expect
是一個請求訊息頭,包含一個期望條件,表示伺服器只有在滿足此期望條件的情況下才能妥善地處理請求。
規範中只規定了一個期望條件,即 Expect: 100-continue
, 對此伺服器可以做出如下回應:
100
如果訊息頭中的期望條件可以得到滿足,使得請求可以順利進行的話,417
(Expectation Failed) 如果伺服器不能滿足期望條件的話;也可以是其他任意表示使用者端錯誤的狀態碼(4xx)。例如,如果請求中 Content-Length
的值太大的話,可能會遭到伺服器的拒絕。
讓使用者端在傳送請求資料之前去判斷伺服器是否願意接收該資料,如果伺服器願意接收,使用者端才會真正傳送資料,如果使用者端直接傳送請求資料,但是伺服器又將該請求拒絕的話,這種行為將帶來很大的資源開銷。
不是所有的伺服器都會正確應答100-continue, 比如lighttpd, 就會返回417 Expectation Failed。
HttpClient
預設攜帶了Expect
頭,我們請求帶上了Expect: 100-continue
的話是不會立刻傳送body中的報文給伺服器,需要伺服器需要對Expect: 100-continue
做出響應,然而對方伺服器不支援Expect
當然不能做出響應,在前言說的問題中,也就是HttpClient
在等對方伺服器響應Expect
,然後再傳送報文,而對方伺服器看來,我們怎麼還不傳送報文過來,雙方都在等資料,最終HttpClient
超時~
以上純屬個人理解,有不正確之處,還請指正~
本文來自部落格園,作者:gui.h,轉載請註明原文連結:https://www.cnblogs.com/springhgui/p/16499439.html