Problem of converting pit'+'to space in http request parameter
I encountered a problem in my work where the parameter carrying the'+'number was converted to a space when we requested the interface of another service through the RestTemplate method provided by SpringBoot
1. Prerequisites
- The first thing we need to know is that the parameters carried in the HTTP request with a'+'request backend are replaced with spaces when tomcat passes.
We arrived through debug
- processParameters method in public final class Parameters class
After layers of dolls he will enter the following methods
- convert method in public final class UDecoder {} class
Here's a closer look https://www.cnblogs.com/thisiswhy/p/12119126.html
As we know above, "+" becomes a space after tomcat processing
2. Understanding transcoding, decoding
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); /* * output * come+%2Bhere%3D * come +here= * come here=/yuftujy */
tomcat | transcoding | Decode | |
---|---|---|---|
" " | " " | "+" | " " |
"+" | " " | "%2B" | "+" |
"=" | "=" | "%3D" | "=" |
We found a problem with spaces. Instead of becoming the'%20'we expected after transcoding, he became a'+', and the'+' number would become a space again after tomcat?
What problems does this cause, and how does the backend handle spaces?
This is a huge pit!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
3. Discover problems
Now let's combine the issues at work, and on the client side, request additional service parameters with "+" using RestTemplate provided by SpringBoot.
- Knowing that admId has gone through tomcat two times from the beginning to the end of the request
I found three cases in debugging
1. Normal requests: http://localhost:8080/cdata?admId=S9Y +qEERxGVFo0DE8mruGVmVYNJraVwZyijimEv6w=
@GetMapping("/cdata") public Object cdata(@RequestParam String admId) throws Exception{ System.out.println("tomcat Rotate:"+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("Success"); User user = new User(); user.setId(1); user.setName("Wang Zhenwei"); responsVO.setData(user); return responsVO; } //Normal request we can see that the parameter admId turns into a space the first time it passes through tomcat, which is definitely incorrect
2. Second: Add transcode decoding
@GetMapping("/cdata") public Object cdata(@RequestParam String admId) throws Exception{ System.out.println("Before transcoding:"+admId);//Before transcoding: S9Y qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= String encode = URLEncoder.encode(admId, "UTF-8"); System.out.println("After transcoding:"+encode);//After transcoding: S9Y+qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%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("Before decoding:"+admId); //Before decoding: S9Y qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D System.out.println("After decoding:"+URLDecoder.decode(admId,"UTF-8")); //After decoding: S9Y qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= ResponsVO<Object> responsVO = new ResponsVO<>(); responsVO.setCode(200); responsVO.setMsg("Success"); User user = new User(); user.setId(1); user.setName("Wang Zhenwei"); responsVO.setData(user); return responsVO; } } /** * Before transcoding: S9Y qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= * After transcoding: S9Y+qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D * Before decoding: S9Y qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D * After decoding: S9Y qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= */
- Still not, because admId has replaced'+'with a space after the first tomcat, and'=' has been converted to'%3D'as expected, but the space has been converted to'+', which is not what we expected'%20', which results in'+' being replaced with a space the second time the interface is requested.
- Looking at this, is he not at a loss???
3. Third: Transcoding for front-end parameters - S9Y%2BqEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
@GetMapping("/cdata") public Object cdata(@RequestParam String admId) throws Exception{ // admId="admId%2BS9Y%2BqEERxxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D"; System.out.println("Before transcoding:"+admId);//Before transcoding: S9Y+qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= String encode = URLEncoder.encode(admId, "UTF-8"); System.out.println("After transcoding:"+encode);//After transcoding: S9Y%2BqEERxGVFo0DE8mruGVmVYNJraVwZyijimEv6w%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("Before decoding:"+admId); //Before decoding: S9Y%2BqEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D System.out.println("After decoding:"+URLDecoder.decode(admId,"UTF-8")); //After decoding: S9Y+qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= ResponsVO<Object> responsVO = new ResponsVO<>(); responsVO.setCode(200); responsVO.setMsg("Success"); User user = new User(); user.setId(1); user.setName("Wang Zhenwei"); responsVO.setData(user); return responsVO; } /** * Before transcoding: S9Y+qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= * After transcoding: S9Y%2BqEERxGVFo0DE8mruGVmVYNJraVwZyijimEv6w%3D * Before decoding: S9Y%2BqEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D * After decoding: S9Y+qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w= */
- From the output, we can see that after the front-end transcodes the parameters,'+'is converted to'%2B' as expected, after tomcat decoding, it is transcoded, and after other service interfaces transcode it, normal data can be returned.
3D
- Before decoding: S9Y%2BqEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w%3D
- After decoding: S9Y+qEERxGVFo0DE8mruGVGmVYNJraVwZyijimEv6w=
*/
- From the output, we can see that after the front end transcodes the parameters, "+" Converted to "as expected"%2B",In tomcat After decoding, transcode it and return to normal data when it is docked by other service interfaces