http请求参数中巨坑的“+”被转为空格的问题
http请求参数中巨坑的“+”被转为空格的问题我在工作中遇到一个问题,我们在通过 SpringBoot 提供的(客户端请求) RestTemplate 方法去请求其他服务的接口时,所带参数携带 “+” 号被转换成空格的问题1.前提前提我们得先知道HTTP请求中携带的参数中带 “+” 请求后端时,在经过 tomcat 时,会被替换成空格。通过debug我们来到public final class
http请求参数中巨坑的“+”被转为空格的问题
源码:https://gitee.com/qplo/rest-template
,我们在通过 SpringBoot 提供的(客户端请求) RestTemplate 方法去请求其他服务的接口时,所带参数携带 “+” 号被转换成空格的问题
1.前提
- 前提我们得先知道HTTP请求中携带的参数中带 “+” 请求后端时,在经过 tomcat 时,会被替换成空格。
通过debug我们来到
- public final class Parameters 类中的 processParameters 方法
经过层层套娃之后他就会进入下面的方法中
- public final class UDecoder{} 类中的 convert 方法
这里详细看https://www.cnblogs.com/thisiswhy/p/12119126.html
上面我们知道 “+” 在经过 tomcat 处理过后会变成空格
2.了解转码,解码
String encode = URLEncoder.encode("come +here=", "UTF-8");
System.out.println(encode);
String decode = URLDecoder.decode(encode, "UTF-8");
System.out.println(decode);
String decodeDemo = URLDecoder.decode("come +here=/yuftujy", "UTF-8");
System.out.println(decodeDemo);
/*
* 输出
* come+%2Bhere%3D
* come +here=
* come here=/yuftujy
*/
tomcat | 转码 | 解码 | |
---|---|---|---|
“ ” | “ ” | “+” | “ ” |
“+” | “ ” | “%2B” | “+” |
“=” | “=” | “%3D” | “=” |
我们这就发现一个空格的问题,他在转码后没有变成我们所预期的 “%20”,而是变成了 ”+“,而 ”+” 号在经过 tomcat 时又会变成 空格?
这会导致什么问题,后端怎么去处理空格?
这就是一个巨大的坑!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3.发现问题
现在我们结合工作中的问题,在客户端,用SpringBoot提供的 RestTemplate 请求其他服务参数带 ”+“ 的问题、
- 得知 admId 从请求开始到结束一共经过了两次 tomcat
在调试中我发现三种情况
1、正常请求:http://localhost:8080/cdata?admId=S9Y+qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
@GetMapping("/cdata")
public Object cdata(@RequestParam String admId) throws Exception{
System.out.println("tomcat转:"+admId);//S9Y qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
String url="http://localhost:8080/hello";
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("admId", encode);
String urls = uriComponentsBuilder.build().toUriString();
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(null, null);
ResponseEntity<ResponsVO> response = restTemplate.exchange(urls, HttpMethod.GET, requestEntity,
new ParameterizedTypeReference<ResponsVO>() {
});
return response.getBody().getData();
}
@GetMapping("hello")
public Object cdataDemo(String admId) throws Exception{
System.out.println(admId);//S9Y qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
ResponsVO<Object> responsVO = new ResponsVO<>();
responsVO.setCode(200);
responsVO.setMsg("成功");
User user = new User();
user.setId(1);
user.setName("");
responsVO.setData(user);
return responsVO;
}
//正常请求我们可以看到参数 admId 第一次进过tomcat的时候 “+” 就变成了空格,肯定是不对的
2.第二种:加上转码解码
@GetMapping("/cdata")
public Object cdata(@RequestParam String admId) throws Exception{
System.out.println("转码前:"+admId);//转码前:S9Y qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
String encode = URLEncoder.encode(admId, "UTF-8");
System.out.println("转码后:"+encode);//转码后:S9Y+qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
String url="http://localhost:8080/hello";
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("admId", encode);
String urls = uriComponentsBuilder.build().toUriString();
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(null, null);
ResponseEntity<ResponsVO> response = restTemplate.exchange(urls, HttpMethod.GET, requestEntity,
new ParameterizedTypeReference<ResponsVO>() {
});
return response.getBody().getData();
}
@GetMapping("hello")
public Object cdataDemo(String admId) throws Exception{
System.out.println("解码前:"+admId);
//解码前:S9Y qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
System.out.println("解码后:"+URLDecoder.decode(admId,"UTF-8"));
//解码后:S9Y qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
ResponsVO<Object> responsVO = new ResponsVO<>();
responsVO.setCode(200);
responsVO.setMsg("成功");
User user = new User();
user.setId(1);
user.setName("");
responsVO.setData(user);
return responsVO;
}
}
/**
* 转码前:S9Y qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
* 转码后:S9Y+qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
* 解码前:S9Y qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
* 解码后:S9Y qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
*/
- 还是不行,因为 admId经过第一次 tomcat的时候就已经把“+”替换成了空格,转码后“=”被符合预期的转成了"%3D",但是 空格 被转码后又变成了 “+” ,不是我们所预期的“%20”,这导致在第二次请求接口的时候 “+” 又被替换成了空格。
- 这么一看,那他不是无解了吗???
- 还有一种不得已的办法,直接把空格替换成 “+”
或者
3.第三种:前端传参时进行转码-S9Y%2BqEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
@GetMapping("/cdata")
public Object cdata(@RequestParam String admId) throws Exception{
// admId="admId%2BS9Y%2BqEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D";
System.out.println("转码前:"+admId);//转码前:S9Y+qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
String encode = URLEncoder.encode(admId, "UTF-8");
System.out.println("转码后:"+encode);//转码后:S9Y%2BqEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
String url="http://localhost:8080/hello";
UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(url)
.queryParam("admId", encode);
String urls = uriComponentsBuilder.build().toUriString();
HttpEntity<Map<String, Object>> requestEntity = new HttpEntity<>(null, null);
ResponseEntity<ResponsVO> response = restTemplate.exchange(urls, HttpMethod.GET, requestEntity,
new ParameterizedTypeReference<ResponsVO>() {
});
return response.getBody().getData();
}
@GetMapping("hello")
public Object cdataDemo(String admId) throws Exception{
System.out.println("解码前:"+admId);
//解码前:S9Y%2BqEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
System.out.println("解码后:"+URLDecoder.decode(admId,"UTF-8"));
//解码后:S9Y+qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
ResponsVO<Object> responsVO = new ResponsVO<>();
responsVO.setCode(200);
responsVO.setMsg("成功");
User user = new User();
user.setId(1);
user.setName("");
responsVO.setData(user);
return responsVO;
}
/**
* 转码前:S9Y+qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
* 转码后:S9Y%2BqEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
* 解码前:S9Y%2BqEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
* 解码后:S9Y+qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
*/
-
从输出结果中我们可以看出,前端对参数进行转码后,“+” 被符合预期的转成了 “%2B”,进过tomcat的解码之后,再对其进行转码,在其他服务接口对其进行解码之后就能返回正常数据
-
这里又发现一个问题,这次我们可以看到,第一次请求时,前端转码后的参数传到后端时被自动解码了,但是在第二次发起请求时,参数没有被自动解码,需要手动解码???
-
原因:是因为 restTemplate 的问题,因为 restTemplate 发起get请求时,会对参数进行一次编码,tomcat又对其进行解码。但是这时候就会发现既然 restTemplate 会对其进行转码,那为什么还要用 encode 再对其进行一次转码? 因为restTemplate 的编码和 encode有所不同,restTemplate 不会对某些字符进行编码,例如 “+” 等,导致“+”在经过 tomcat 时被转成空格!!!
https://www.jianshu.com/p/0bdcc6836eb3
总结: -
前端传参到后端,参数首先会经过 tomcat 的检验,tomcat 的 convert 的方法会对参数进行解码,并把“+”提换成空格
-
如果用 restTemplate 去请求其他服务时,参数又会经过 restTemplate 的检验,restTemplate 会对其进行编码,但是有一点不同的是,不会对某些特殊字符进行编码。例如 “+” 等。
-
请求到其他服务时参数又来到了 tomcat 这里经受检验,然后来到 controller 层
用 restTemplate 请求其他服务 的参数带有特殊字符时
解决办法:
1.
- 前端对参数进行转码(把“+”转化为“%2B”)
- 后端对参数进行一次转码,
- 其他服务进行解码
前端转两次码
其他服务进行解码
开放原子开发者工作坊旨在鼓励更多人参与开源活动,与志同道合的开发者们相互交流开发经验、分享开发心得、获取前沿技术趋势。工作坊有多种形式的开发者活动,如meetup、训练营等,主打技术交流,干货满满,真诚地邀请各位开发者共同参与!
更多推荐
所有评论(0)