我們用Java開發項目時,發送請求都是用的RestTemplate。最近和其他部門合作時,我們需要請求他們的一個http接口。兩邊協議都確定好后,發現聯調不通。后來發現是我們這邊發出的請求,到達對方那邊時,他們接收到的是經過了urlencode后的結果,通過wireshark抓包也看到確實發出的請求是被urlencode的。
我們這邊的進程,並沒有顯式調用urlencode相關的方法,因此猜測是RestTemplate自動給我們進行了urlencode。網上查找資料,發現這兩篇文章講得很詳細:
https://blog.csdn.net/Petershusheng/article/details/54236816
RestTemplate確實在底層自動給我們進行了urlencode,不過我們也可以通過UriComponentsBuilder來構建URI對象,手動選擇不進行urlencode,具體操作方式可以參考這個文章:
https://blog.csdn.net/blueheart20/article/details/80916517
1 |
riComponentsBuilder builder = UriComponentsBuilder |
不過一般接口調用,都應該進行urlencode,避免一些特殊字符在傳遞過程中出現問題。因此回到我們這個應用,最終還是聯系接口提供方,在接收請求時,對所有參數都進行了urldecode。
RestTemplate中encode請求參數的原理
我的請求參數里面,有個sign參數,其值含有一些特殊符號,比如斜杠(/)和等號(=),經過RestTemplate處理並發送請求后,我抓包發現請求中的斜杠沒有被urlencode,但是等號卻被urlencode了,這是為什么呢?RestTemplate的urlencode難道不是通用的,而是自定義的么?
下午花了2個小時,通過IDE斷點跟蹤調試源碼,終於弄清楚原因了。
原因
在HierarchicalUriComponents的大約234行,有個encodeUriComponent方法,這個方法就是RestTemplate用來對url中的參數進行encode處理的邏輯:
1 |
static String (String source, Charset charset, HierarchicalUriComponents.Type type) { |
從代碼可以看到,encode的過程,是先將參數轉為byte數組,然后逐個byte進行檢查,如果發現某個byte不在url參數允許的范圍內,則對其進行encode操作。
然后我們找到上面的HierarchicalUriComponents.Type.QUERY_PARAM的代碼,大約在HierarchicalUriComponents的811行:
1 |
QUERY_PARAM { |
從上面的邏輯可以看到,如果字符是斜杠(ASCII碼等於47),那么進程會判斷這個字符是在url參數中被允許的,則不進行encode了;而等號(ASCII碼為61)被其判斷為是一個不被允許的url字符,因此就會被encode。
至此,原因終於找到了。
解決方案
解決方案也比較簡單,因為我們可以自行選擇要不要讓RestTemplate對我們的參數進行urlencode,所以我們可以先自己手動將url進行encode,然后發送請求時,選擇不讓RestTemplate自動做encode即可。
這是處理參數的方法:
1 |
/** |
這是發送請求的代碼:
1 |
String requestUrl = "http://www.10jqka.com.cn/" |