開發過程中,發現+(加號)這個符號沒有轉義,導致再調用服務的時候把加號轉義成空格了。導致後臺獲取到的數據會不正確。
1. 問題發現過程
feign 解析參數的時候,使用的標準是 RFC 3986
,這個標準的加號是不需要被轉義的。其具體的實現是 feign.template.UriUtils#encodeReserved(String value, String reserved, Charset charset)
2. 解決辦法
feign 調用過程
1. feign核心先將(定義好的feign接口)接口中的參數解析出來
2. 對接實際參數和接口參數(入參調用的參數)
3. 對入參的參數進行編碼(UriUtils#encodeReserved)(問題出在這裏)
4. 調用註冊的 RequestInterceptor(自定義)
5. Encoder 實現類,這裏是body裏面的內容纔會有調用(自定義)
6. 具體的http網絡請求邏輯
依據上面的過程,我們可以實現一個 RequestInterceptor 攔截器,在這裏對參數再次進行轉義即可。
public void apply(RequestTemplate template) {
Map<String, Collection<String>> _queries = template.queries();
if (!_queries.isEmpty()) {
//由於在最新的 RFC 3986 規範,+號是不需要編碼的,因此spring 實現的是這個規範,這裏就需要參數中進行編碼先,兼容舊規範。
Map<String, Collection<String>> encodeQueries = new HashMap<String, Collection<String>>(_queries.size());
Iterator<String> iterator = _queries.keySet().iterator();
Collection<String> encodeValues = null;
while (iterator.hasNext()) {
encodeValues = new ArrayList<>();
String key = iterator.next();
Collection<String> values = _queries.get(key);
for (String _str : values) {
_str = _str.replaceAll("\\+", "%2B");
encodeValues.add(_str);
}
encodeQueries.put(key, encodeValues);
}
template.queries(null);
template.queries(encodeQueries);
}
}
上面是代碼片段,詳細請查看 FeignRequestInterceptor.java
3. 疑問
3.1 是否可以使用 HTTPClient 的實現就可以解決問題?
也不行,如果不做上面的實現,直接改用HTTPClient實現的話,也只是在發送的過程中起到作用,還是需要在前進行處理。
4. 參考文獻
https://spring.hhui.top/spring-blog/2019/03/27/190327-Spring-RestTemplate之urlencode參數解析異常全程分析/
https://www.w3school.com.cn/tags/html_ref_urlencode.html