8

I use @RequestParam to get the parameter value,but I find the if I pass the value like 'name=abc&def&id=123',I will get the name value 'abc' instead of 'abc&def'. I find the encode and decode the parameter value can solve my problem.But I have to write the encode and decode mehtod in every controller method,Do spring have the global mehtod that decode every @RequestParam value?When using @RequestParam, is it necessary to encode and decode every value?

Here is my code:

@PostMapping("/getStudent")
public Student getStudent(
        @RequestParam String name,
        @RequestParam String id) { 
        name= URLDecoder.decode(name, "UTF-8");  
        //searchStudent
        return Student;
}

@PostMapping("/getTeacher")
public teacher getTeacher(
        @RequestParam String name,
        @RequestParam String teacherNo) { 
        name= URLDecoder.decode(name, "UTF-8");  
        //searchTeacher
        return teacher;
}

Somebody say the the Spring will have already done this,but I have try,the result is not right.Only use curl cmd is ok,but java code is not ok.

@PostMapping(value = "/example")
public String handleUrlDecode1(@RequestParam String param) { 
    //print ello%26test
    System.out.println("/example?param received: " + param); 
    return "success";
}

@GetMapping(value = "/request")
public String request() {
    String url =  "http://127.0.0.1:8080/example?param=ello%26test";
    System.out.println(url);
    RestTemplate restTemplate = new RestTemplate();
    return restTemplate.postForObject(url, null, String.class);
}
Rebecca
  • 199
  • 2
  • 8
  • If you pass `name=abc&def&id=123` then you have parameters `name`, `def` and `id`, not just `name` and `id`. If you want `name` to have value `abc&def`, then the `&` needs to be encoded as `%26`, so the value is `abc%26def`. The call the `URLDecoder.decode` shouldn't be necessary, as Spring will have already done this for you. – Mark Rotteveel Jul 19 '22 at 13:57
  • @MarkRotteveel,I have to call URLDecoder.decode,otherwise I will get abc%26def, I use `restTemplate.postForObject` to send the request – Rebecca Jul 19 '22 at 14:51
  • See updates on my answer. The issue you are seeing is RestTemplate is automatically url encoding html entities for you. If you pass "&" it will encode it, and the Controller will also automatically decode it too. If you pass "%26" it passes it as a literal string containing "%26" – Kevin Hooke Jul 21 '22 at 16:48

3 Answers3

2

As you can read here, the escape character for & is %26.

So you should use the following

name=abc%26def&id=123

If you don't use an escape character according to URL standards, Spring will try to use what follows & and try to match it as a new query parameter.

Panagiotis Bougioukos
  • 15,955
  • 2
  • 30
  • 47
2

You must create an HTTP entity and send the headers and parameter in body.

@GetMapping(value = "/request")
public String request()  {
    String url =  "http://127.0.0.1:8080/example";
    System.out.println(url);
    RestTemplate restTemplate = new RestTemplate(); 
    HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    MultiValueMap<String, String> map= new LinkedMultiValueMap<String, String>();
    map.add("param","ello&test");
    map.add("id","ab&c=def");
    HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers); 
    return restTemplate.postForObject(url, request, String.class);
}
flower
  • 2,212
  • 3
  • 29
  • 44
1

No need to manually use URLDecoder, SpringBoot controllers will handle it for you.

@RestController
public class UrlDecodeController {

    @GetMapping(value = "/example")
    public String handleUrlDecode(@RequestParam String param) {
    
        System.out.println("/example?param received: " + param);
    
        return "success";
    }

    @PostMapping(value = "/example2")
    public String handleUrlDecodeInPostRequest(@RequestParam String param1, ExamplePayload payload) {
        System.out.println("/example2?param1 received: " + param1);
        System.out.println("request body - value1: " + payload.getValue1());
    
        return "success";
    }

    @GetMapping(value = "/request")
    public String request() {
        String url =  "http://localhost:8080/example2?param1=test1&test2";
        System.out.println(url);
        RestTemplate restTemplate = new RestTemplate();
    
        HttpHeaders headers = new HttpHeaders();
    headers.setContentType(MediaType.APPLICATION_FORM_URLENCODED);
    
        MultiValueMap<String, String> map = new LinkedMultiValueMap<String, String>();
        map.add("value1","test1&test2");
    
        HttpEntity<MultiValueMap<String, String>> request = new HttpEntity<MultiValueMap<String, String>>(map, headers); 
    
        return restTemplate.postForObject(url, request, String.class);
    }


    class ExamplePayload{
    
        private String value1;
        private String value2;
    
        //getters and setters

        public ExamplePayload() {
        }
    }
}

Call with GET /example?param=hello%26test and the System.out.println outputs:

/example?param received: hello&test

Call the POST using curl as an example:

curl -X POST "http://localhost:8080/example2?param1=test1%26test2" -d "value1=test3%26test4"

Prints:

/example2?param1 received: test1&test2

request body - value1: test3&test4

Added GET /request to show using RestTemplate with the application/x-www-form-urlencoded Content-Type. Note that RestTemplate will automatically url encode any values passed as request parameters or in the request body. If you pass a String value of "%26" it will pass it as is, this is what you are seeing in your example. If you pass "&" it will url encode it to "%26" for you, and the Controller decodes it automatically on the other side.

Kevin Hooke
  • 2,583
  • 2
  • 19
  • 33