1. 程式人生 > 其它 >我都調通了,為啥你不行?

我都調通了,為啥你不行?

原創:打碼日記(微信公眾號ID:codelogs),歡迎分享,轉載請保留出處。

場景

有時我們開發了一個api介面,自己調得好好的,介面文件也寫好了,但別人呼叫時就是有問題,而當我們除錯時,發現請求根本沒進來或進來了卻取不到呼叫引數,該怎麼告知對方呼叫姿勢哪不對呢?

解決方法

對於編碼新手,一般會和對方撕扯一會,然後甚至去review對方的程式碼,這樣也許能解決問題,但自己畢竟不熟悉別人系統的實現,耗費時間較長。

另外api呼叫端可能因為封裝方式不同或者加過一些攔截器,導致你看呼叫端的程式碼根本看不到什麼問題,或者因為呼叫引數有一些不易分辨或不可見的特殊字元,讓你無法察覺到這裡有問題。

其實這種問題,我們從網路層出發,對比自己正確呼叫時的資料包與對方錯誤呼叫時的資料包內容,以此來診斷問題所在更加高效,畢竟任何封裝或攔截器的處理,最終都會反饋在底層的互動資料上。

這裡,我使用springmvc開發一個簡單的介面,如下:

@RestController
public class TestController {
    @RequestMapping(value = "/test",produces = MimeTypeUtils.TEXT_PLAIN_VALUE)
    @ResponseBody
    public String test(@RequestParam(name = "name") String name){
       return name;
    }
}

這個介面就是直接將name引數的值返回了,然後我使用curl模擬正確與錯誤的呼叫方式,如下:

#正確呼叫方式
$ curl http://localhost:8081/test --data-urlencode 'name=a+b'
a+b
#錯誤呼叫方式
$ curl http://localhost:8081/test --data 'name=a+b' 
a b

假設呼叫方的程式碼實現的邏輯類似上面錯誤的呼叫方式,傳遞name引數為a+b,得到的卻是a b,接下來我們來定位看看,錯誤的呼叫方法問題在哪?

方法1:使用wireshark抓包軟體
開啟wireshark,先後抓包兩次正常的以及不正常的請求資料,拿到請求資料後通過文字差異對比工具來對比。

  1. 如下為wireshark兩次抓包結果,使用追蹤流 -> TCP,檢視HTTP請求資料
  2. 如下為正確的HTTP請求資料,其中紅色為請求資料,藍色為響應資料
  3. 如下為錯誤的HTTP請求資料
  4. 對比請求資料差異

可以看出,一個請求name引數為a%2Bb,一個請求name引數為a+b,顯然這是由於name引數值沒有url_encode導致的。
另外或許你會奇怪,為啥錯誤請求值為a+b,為啥程式碼裡面獲取到的卻是a b? 原因是tomcat接收到請求引數後,會做一次url_decode,而+號會被decode為空格,java的URLDecoder.decode("a+b", "UTF-8")也是如此。

方法2:使用socat命令
在抓包工具無法使用的情況下,可以嘗試socat命令,使用socat命令來中轉請求,呼叫端將請求先發給socat,socat再把請求轉給服務端,如下:

# 使用socat中轉請求
$ socat -v TCP4-LISTEN:8080,bind=0.0.0.0,reuseaddr,fork TCP4:localhost:8081

# 呼叫端訪問socat監聽的8080埠
# 正確呼叫方式
$ curl http://localhost:8080/test --data-urlencode 'name=a+b'
a+b
# 錯誤呼叫方式
$ curl http://localhost:8080/test --data 'name=a+b' 
a b

再去看socat,會發現如下結果:

其中socat新增-v引數後,會自動將中轉的資料流以明文顯示出來,其中類似> 2020/10/11 13:05:03.536294 length=162 from=0 to=161之後的部分,就是請求資料,而類似< 2020/10/11 13:05:03.740585 length=116 from=0 to=115之後的部分,就是響應資料,同樣的,你可以將兩次請求資料複製到文字對比工具中去發現差異。

有時這種呼叫差異是特殊字元導致的,比如空白字元、零寬字元,上面的方式可能看不出差異,這時你可以將-v引數替換為-x引數,來對比資料的十六進位制,同樣的wireshark也可以檢視資料包的十六進位制,相信你摸索一下也可以找到。

舉一反三

此外,有些時候,我們寫的程式碼去查詢資料庫時,查不到資料,但我們把SQL拿到資料庫客戶端工具中,卻可以查到資料,這種問題極有可能是SQL被底層一些攔截器改寫了,這時,我們也可以使用上面的方法來確認,這裡僅僅介紹使用socat的方式,如下,使用socat中轉資料庫連線:

# 1.中轉資料庫連線
$ socat -v TCP4-LISTEN:3307,bind=0.0.0.0,reuseaddr,fork TCP4:localhost:3306 2>&1 | tee data.log
# 2.然後我們程式碼中連線資料庫的地方,將埠改成3307,然後執行我們的SQL查詢

# 3.檢視真實發送給資料庫的SQL
cat data.log |grep -i 'select'

結果如下:

我相信到這一步,大概能發現SQL差異了,接下來就是找到底層修改SQL的程式碼在哪,以及如何處置了。

總結

遇到這種網路呼叫上的差異問題,多多考慮使用wireshark、socat、ncat之類的網路工具來處理,相信問題處理效率會大大增加。

往期內容

還在胡亂設定連線空閒時間?
使用socat批量操作多臺機器