16

I have the following method in my Spring MVC @Controller :

@RequestMapping(method = RequestMethod.GET)
public String testUrl(@RequestParam(value="test") Map<String, String> test) {   
    (...)
}

I call it like this :

http://myUrl?test[A]=ABC&test[B]=DEF

However the "test" RequestParam variable is always null

What do I have to do in order to populate "test" variable ?

Majid Roustaei
  • 1,556
  • 1
  • 20
  • 39
Christos Loupassakis
  • 1,216
  • 3
  • 16
  • 23
  • Did you resolve this? If so can you post your answer? – Federico Piazza Jul 26 '18 at 15:10
  • Sorry for this late answer, but I can't help anyway. It's been over a year I worked in the project, and from what I remember I couldn't find a viable solution, so I just dropped this part and found an alternative way to do what I was supposed to do. Steel waiting for a solution – Christos Loupassakis Dec 19 '18 at 21:26

6 Answers6

11

As detailed here https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html

If the method parameter is Map or MultiValueMap and a parameter name is not specified, then the map parameter is populated with all request parameter names and values.

So you would change your definition like this.

@RequestMapping(method = RequestMethod.GET)
public String testUrl(@RequestParam Map<String, String> parameters) 
{   
  (...)
}

And in your parameters if you called the url http://myUrl?A=ABC&B=DEF

You would have in your method

parameters.get("A");
parameters.get("B");
zatopek
  • 333
  • 2
  • 8
  • 1
    I don't need to retrieve all the request parameters, that's why I added (value="test"). And even when retrieving them like that, the url "test[A]=ABC&test[B]=DEF" gives me "test[A]" as key and not just "A" – Christos Loupassakis Nov 21 '17 at 23:46
3

You can create a new class that contains the map that should be populated by Spring and then use that class as a parameter of your @RequestMapping annotated method.

In your example create a new class

public static class Form {
   private Map<String, String> test;
   // getters and setters
}

Then you can use Form as a parameter in your method.

@RequestMapping(method = RequestMethod.GET)
public String testUrl(Form form) {
  // use values from form.getTest()
}
user2456718
  • 216
  • 1
  • 4
2

Spring doesn't have default conversion strategy from multiple parameters with the same name to HashMap. It can, however, convert them easily to List, array or Set.

@RequestMapping(value = "/testset", method = RequestMethod.GET)
    public String testSet(@RequestParam(value = "test") Set<String> test) {

        return "success";
    }

I tested with postman like http://localhost:8080/mappings/testset?test=ABC&test=DEF

You will see set having data, [ABC, DEF]

Amit K Bist
  • 6,760
  • 1
  • 11
  • 26
2

Your question needs to be considered from different points of view.

  1. first part:

as is mentioned in the title of the question, is how to have Map<String, String> as @RequestParam.

Consider this endpoint:

@GetMapping(value = "/map")
public ResponseEntity getData(@RequestParam Map<String, String> allParams) {
    String str = Optional.ofNullable(allParams.get("first")).orElse(null);
    return ResponseEntity.ok(str);
}

you can call that via:

http://<ip>:<port>/child/map?first=data1&second=data2

then when you debug your code, you will get these values:

> allParams (size = 2)
    > first = data1
    > second = data2

and the response of the requested url will be data1.


  1. second part:

as your requested url shows (you have also said that in other answers' comments) ,you need an array to be passed by url.

consider this endpoint:

public ResponseEntity<?> getData (@RequestParam("test") Long[] testId,
                                  @RequestParam("notTest") Long notTestId)

to call this API and pass proper values, you need to pass parameters in this way:

?test=1&test=2&notTest=3

all test values are reachable via test[0] or test[1] in your code.


  1. third part:

have another look on requested url parameters, like: test[B]

putting brackets (or [ ]) into url is not usually possible. you have to put equivalent ASCII code with % sign.

for example [ is equal to %5B and ] is equal to %5D.

as an example, test[0] would be test%5B0%5D.

more ASCII codes on: https://ascii.cl/

Majid Roustaei
  • 1,556
  • 1
  • 20
  • 39
2

I faced a similar situation where the client sends two groups of variable parameters. Let's call these groups foo and bar. A request could look like:

GET /search?page=2&size=10&foo[x]=aaa&foo[y]=bbb&bar[z]=ccc

I wanted to map these parameters to two distinct maps. Something like:

@GetMapping("/search")
public Page<...> search(Pageable pageable,
        @RequestParam(...) Map<String, String> foo,
        @RequestParam(...) Map<String, String> bar) {
    ...
}

@RequestParam didn't work for me, too. Instead I created a Model class with two fields of type Map<> matching the query parameter names foo and bar (@Data is lombok.Data).

@Data
public static class FooBar {
    Map<String, String> foo;
    Map<String, String> bar;
}

My controller code has changed to:

@GetMapping("/search")
public Page<...> search(Pageable pageable, FooBar fooBar) {
    ...
}

When requesting GET /search?page=2&size=10&foo[x]=aaa&foo[y]=bbb&bar[z]=ccc Spring instantiated the Maps and filled fooBar.getFoo() with keys/values x/aaa and y/bbb and fooBar.getBar() with z/ccc.

Steffen Bobek
  • 582
  • 1
  • 5
  • 18
1

you can use MultiValueMap

MultiValueMap<String, String>

@RequestMapping(method = RequestMethod.GET)
public String testUrl(@RequestParam(value="test") MultiValueMap<String, String> test) {   
    (...)
}

and while testing don't use test[A],test[B]. just use it as stated below.

http://myUrl?test=ABC&test=DEF

test result will be in below format when you print it.

test = {[ABC, DEF]}

Ravi
  • 338
  • 1
  • 12