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