RestTemplate发送http请求时,get方式携带参数的特殊字符编码问题记录
问题:
项目中需要对接其他项目-使用RestTemplate发送http请求;
get方式url后拼接参数方式与预期结果不符;
工具类代码如下:
/**
* 发送get请求
*
* @param url
* @param params
* @param header
* @return
*/
public static String getHttplient(String url, Map<String, Object> params, Map<String, String> header) {
HttpHeaders headers = new HttpHeaders();
if (!CollectionUtils.isEmpty(header)) {
// 填充header
for (Map.Entry<String, String> entry : header.entrySet()) {
headers.add(entry.getKey(), entry.getValue());
}
}
HttpEntity<Map<String, Object>> entry = new HttpEntity<>(params, headers);
log.info("流水号:{},调用外部接口,url:{},参数:{},header:{}", TLocalHelper.getSeq(), url, JsonUtil.toJsonString(params), header);
ResponseEntity<String> result = restTemplate.getForEntity(url, String.class, params);
log.info("流水号:{},返回结果:{}", TLocalHelper.getSeq(), result.getBody());
return result.getBody();
}
首先:
确定对方提供接口以及测试参数无误:
然后:
工具类代码如下:
@Test
public void test111() throws Exception {
String token = "cUJ%2FFaFkoEVx5XNRGCm1ZivqNoJqhyuQQL6GhywoMKusSAHujQsoUMWnKgjBFp3L";
String url = tokenUrl + "?token=" + token;
String s = HttpUtil.getHttplient(url, new HashMap<>(), new HashMap<>());
}
tokenUrl是从配置文件中加载的;
运行结果:
- 对比分析:
工具类发送的url地址和在浏览器里输入的地址,完全一致,但是一个成功,一个失败;
那么:问题定位:工具类的问题
在查询了一些资料和源码发现:
RestTemplate在发送string类型的参数请求时,将url给encode了;那么就造成了实际上发送的url参数和我们放进去的值不一样;所以造成了错误;
同时我们也发现:
在发送URI类型的参数请求时,是直接发送的,
那此时,我们应该有两种方案去解决此问题:
- 通过传URI类型的参数方式实现;(推荐此方案)
- 在发送请求前;先将我们的参数decode掉,这样,保证发送的请求经过底层的encode时,不会多次encode,以引发实际上发送的url参数和我们放进去的值不一样;(此方案可能存在bug,所以推荐方案一)
解决方案一: 通过传URI类型的参数方式实现;
添加工具类方法(通过URI方式传参):
public static String getHttplienturi(URI url, Map<String, Object> params, Map<String, String> header) {
HttpHeaders headers = new HttpHeaders();
if (!CollectionUtils.isEmpty(header)) {
// 填充header
for (Map.Entry<String, String> entry : header.entrySet()) {
headers.add(entry.getKey(), entry.getValue());
}
}
HttpEntity<Map<String, Object>> entry = new HttpEntity<>(params, headers);
log.info("流水号:{},调用外部接口,url:{},参数:{},header:{}", TLocalHelper.getSeq(), url, JsonUtil.toJsonString(params), header);
String result = restTemplate.getForObject(url, String.class);
log.info("流水号:{},返回结果:{}", TLocalHelper.getSeq(), result);
return result;
}
调用代码:
使用UriComponentBuilder构造URI对象
@Test
public void test111() throws Exception {
String token = "cUJ%2FFaFkoEVx5XNRGCm1ZivqNoJqhyuQQL6GhywoMKusSAHujQsoUMWnKgjBFp3L";
UriComponentsBuilder builder = UriComponentsBuilder
.fromHttpUrl(tokenUrl).queryParam("token", token);
URI uri = builder.build(true).toUri();
String s = HttpUtil.getHttplienturi(uri, new HashMap<>(),new HashMap<>());
}
结果:
完美解决;
解决方案二: 先将我们的参数decode,再发送请求;
代码如下:
@Test
public void test111() throws Exception {
String token = "cUJ%2FFaFkoEVx5XNRGCm1ZivqNoJqhyuQQL6GhywoMKusSAHujQsoUMWnKgjBFp3L";
token = URLDecoder.decode(token,"UTF-8");
String url = tokenUrl + "?token=" + token;
String s = HttpUtil.getHttplient(url, new HashMap<>(), new HashMap<>());
}
结果:
注意:
在查询文档时,发现有人用此方案仍然无法解决,但是我这边没有出现此问题,所以没有记录;
推荐使用方案一;此方案留做记录;
PS:
另外:
我个人觉得,其实对方设计的此种方案并不是太合理;
像这种问题,只会在get方式请求,并且参数是通过url拼接方式传递才会出现;
在一开始设计时,设计成通过post方式请求,application/json方式携带参数,可完美避免此问题;
但是,
我们接对方接口,不能出现问题就要求对方修改代码,这是不现实的,我们也要可以通过修改自己代码的方案来解决bug;