RestTemplate 請求url中包含百分號 會被轉義成25
最初使用RestTemplate 進行遠程調用方法如下:
1
2
3
4
5
6
7
8
|
private String getRemoteData(String url) { logger.info( "Request URL :" + url + "|" ); String resp = rest.getForObject(url, String. class ); logger.info( "Response result : " + resp.toString()); return resp; } |
但發現請求結果一直為空。
最后發現由于我們的業務場景中,請求參數包含中文要求按指定規則轉碼,導致請求url中包含% ,而RestTemplate會自動調用encode方法進行轉義,將%轉義成了%25 。
解決方法
自建URI 傳入:
1
2
3
4
5
6
7
8
9
10
11
12
13
|
private String getRemoteData(String url) { logger.info( "Request URL :" + url + "|" ); String resp = null ; try { URI uri = new URI(url); resp = rest.getForObject(uri, String. class ); } catch (URISyntaxException e) { logger.error( "Create URI Exception !" ); } logger.info( "Response result : " + resp.toString()); return resp; } |
RestTemplate轉碼bug
發現一個關于HTTP的Get請求的罕見bug。
轉碼問題的背景
需要向tigergraph服務端發送一個復雜的get請求,參數只有一個,但是參數的值是一個復雜json
服務端收到的值始終是不正常的值。觀察發現,不正常地方在于服務端本應解析為空格的地方都變成了加號(+)。
以為是代碼寫得有問題,然后使用HTTPclient的原生的方式發起請求:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
public static String doGet(String url) throws Exception{ HttpGet get = new HttpGet(url); return doMethod(get); } private static String doMethod(HttpRequestBase method) throws Exception{ CloseableHttpResponse response = null ; CloseableHttpClient client; HttpClientBuilder hcb = HttpClientBuilder.create(); HttpRequestRetryHandler hrrh = new DefaultHttpRequestRetryHandler(); HttpClientBuilder httpClientBuilder = hcb.setRetryHandler(hrrh); client = httpClientBuilder.build(); method.addHeader( "User-Agent" , "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1;SV1)" ); method.addHeader(HTTP.CONTENT_TYPE, APPLICATION_JSON); RequestConfig.Builder confBuilder = RequestConfig.custom(); confBuilder.setConnectTimeout(CONNECT_TIMEOUT); confBuilder.setConnectionRequestTimeout(REQUEST_TIMEOUT); confBuilder.setSocketTimeout(SOCKET_TIMEOUT); RequestConfig config = confBuilder.build(); method.setConfig(config); response = client.execute(method); int code = response.getStatusLine().getStatusCode(); String result = EntityUtils.toString(response.getEntity()); response.close(); client.close(); return result; } |
得到結果還是這個問題,使用Assured測試工具構建http請求也有這問題。
結論
后來仔細檢查了URLEncode.encode方法和RestTemplate源碼實現后,發現是客戶端的轉碼協議和服務端的解碼協議不匹配導致。
經反復測試和嚴重,這個問題只有參數中帶有空格時才會有,其他字符都不有,比如: / * & 這類特殊字符都沒這問題。
最后的解決方案是替換URL串的轉碼后的字符串中的空格為%20,然后使用http client原生的請求方式。
第二個解決方案是使用RestTemplate的UriComponentsBuilder類,使用(builder.build(false).toUri()獲得URL,參數必須是false才會把空格轉成%20
1
2
3
4
5
6
7
8
9
10
11
|
/** * urlencode轉碼不能隨便用,因為她會把空格轉換成+號,而不是標準的%20字符。 * 對于spring構建的服務端不會有這個問題。但我在tiger服務器上遇到這種問題。 * 所以urlencode只適用于服務端支持的協議是RFC1738 * 如果服務端只支持RFC 2396標準,那么服務端解碼時,會把加號+當成保留字符,而不轉碼 * */ @Override @SuppressWarnings ( "all" ) public <Req, Resp> Resp doGet(String url, Req request, Class<Resp> responseType) throws Exception { UriComponentsBuilder builder = UriComponentsBuilder.fromHttpUrl(url); Map<String, Object> parameters = (Map<String, Object>)request; for (Map.Entry<String, Object> entry : parameters.entrySet()) { builder.queryParam(entry.getKey(), Objects.toString(entry.getValue(), "" )); } return restTemplate.getForObject(builder.build( false ).toUri(), responseType); } |
為什么會有這個問題?
根源在于Java語言的URLEncode類只能適用于早期的RFC協議,通常spring開發的服務端是兼容這種模式的。
新版的RFC協議會把+號當成關鍵字不再反轉成空格,這通常體現在新技術上,比如目前用的tigergraph圖數據庫就有這情形。
以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。
原文鏈接:https://blog.csdn.net/toneylyx/article/details/98088637