# RestTemplate **Repository Path**: qplo/rest-template ## Basic Information - **Project Name**: RestTemplate - **Description**: No description available - **Primary Language**: Java - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2021-12-26 - **Last Updated**: 2022-02-22 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # http请求参数中巨坑的“+”被转为空格的问题 ​ 我在工作中遇到一个问题,我们在通过 SpringBoot 提供的(客户端请求) RestTemplate 方法去请求其他服务的接口时,所带参数携带 “+” 号被转换成空格的问题 ## 1.前提 - 前提我们得先知道HTTP请求中携带的参数中带 “+” 请求后端时,在经过 tomcat 时,会被替换成空格。 通过debug我们来到 - public final class Parameters 类中的 processParameters 方法 ![image-20211225232948655](C:\Users\deku\AppData\Roaming\Typora\typora-user-images\image-20211225232948655.png) 经过层层套娃之后他就会进入下面的方法中 - public final class UDecoder{} 类中的 convert 方法 ![image-20211225232414184](C:\Users\deku\AppData\Roaming\Typora\typora-user-images\image-20211225232414184.png) 这里详细看https://www.cnblogs.com/thisiswhy/p/12119126.html 上面我们知道 “+” 在经过 tomcat 处理过后会变成空格 ## 2.了解转码,解码 ```java 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= ```java @GetMapping("/cdata") public Object cdata(@RequestParam String admId) throws Exception{ System.out.println("tomcat转:"+admId);//admId S9Y+qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= String url="http://localhost:8080/hello"; UriComponentsBuilder uriComponentsBuilder = UriComponentsBuilder.fromHttpUrl(url) .queryParam("admId", encode); String urls = uriComponentsBuilder.build().toUriString(); HttpEntity> requestEntity = new HttpEntity<>(null, null); ResponseEntity response = restTemplate.exchange(urls, HttpMethod.GET, requestEntity, new ParameterizedTypeReference() { }); return response.getBody().getData(); } @GetMapping("hello") public Object cdataDemo(String admId) throws Exception{ System.out.println(admId);//S9Y qEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= ResponsVO 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.第二种:加上转码解码 ```java @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> requestEntity = new HttpEntity<>(null, null); ResponseEntity response = restTemplate.exchange(urls, HttpMethod.GET, requestEntity, new ParameterizedTypeReference() { }); 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 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 ```java @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> requestEntity = new HttpEntity<>(null, null); ResponseEntity response = restTemplate.exchange(urls, HttpMethod.GET, requestEntity, new ParameterizedTypeReference() { }); 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 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的解码之后,再对其进行转码,在其他服务接口对其进行接码之后就能返回正常数据