Spring MVC populate #RequestParam Map<String, String> - java

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 ?

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");

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()
}

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]

Your question needs to be considered from different points of view.
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.
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.
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/

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.

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]}

Related

Setting multiple parameters in a request

I need to form a cache, for regions by id, which are passed in the region_ids parameter, the request looks like this:
localhost:8080/cache/mscache?region_ids=5,19....,23
how is it best to read these several parameters in the program code?
read them a String and the parse that String into whatever you want:
#GetMapping("/cache/mscache)
public String getCache(#RequestParam String listOfRegionIds)
List<String> ids = Arrays.stream(listOfRegiosIds.split(",")).collect(Collectors.toList);
// ...
}
more info at https://www.baeldung.com/spring-request-param
You can use an array or a List
#GetMapping(value = "/test")
public void test(#RequestParam List<String> ids) {
ids.forEach(System.out::println);
}
Make a get request like:
http://localhost:8080/test?ids=1,2,3
Check here for more details.
If the request is a Get request use #RequestParam like sugessted by J Asgarov, if it is soething else you can also use #RequestBody by creating an class containing all of your parameters
for exemple a Post request could look like this :
#PostMapping("...")
public String postCache(#RequestBody RegionIdsRequest regionIds)
// ...
}
public class RegionIdsRequest{
List<int> regionsIds = //...
}

How to pass multiple Map/List/Array from API url to RestController in Java?

I have a method which accepts an ArrayList, HashMap and a List as argument and other parameters as well.
methodName(String request,
ArrayList<String> referenceList,
HashMap<String, String> params,
Map<String, List<?>> inClause,
boolean isClassicFlatSuggestionBox) throws Exception {};
Now I know how to accept normal string parameters and a single Map/List from url.
http://localhost:8888/restApi/getSuggestionData/sa/warehouse?number=20,age=4
This url returns 2 Path variable String parameter and a map of number and age at Controller like this
public ResponseEntity<Collection<?>> getSuggestionList(#PathVariable("request") String request,#PathVariable("reference") String reference, #RequestParam HashMap<String, String> params)
But how to pass multiple maps ?
I can't take everything in a single map and then play with it at Controller side.
is there any way to get multiple map/List directly at Controller side ?
I suggest to change this to POST and add payload that contains necessary objects. Then your controller method will look like:
#POST
public ResponseEntity<Collection<?>> getSuggestionList(
#PathVariable("request") String request, #PathVariable("reference") String reference, #RequestBody() Payload payload) { }
And the Payload class contains the list you want:
public class Payload {
#XmlElement
private ArrayList<String> referenceList;
#XmlElement
private HashMap<String, String> params;
// getters and setters here or Lombok :)
}

Spring #RequestParam - Mixing named params and Map<String,String> params

I'm writing a Spring Boot application which receives parameters via REST endpoint and forwards them to another system. The received params contain some known fields but may also contain multiple variable fields starting with filter followed by an undefined name:
example.com?id=1&name=foo&filter1=2&filterA=B&[...]&filterWhatever=something
As you can see there are params id and name, and multiple params starting with filter. When calling the target system, I need to remove filter from the param keys and use everything after that as key:
targetsystem.com?id=1&name=foo&1=2&A=B&[...]&whatever=something (no more filter in keys)
This itself is not a problem, I can just just #RequestParam Map<String, String> params, stream/loop the params and modify as I want. But using Swagger as API documentation tool, I want to list all known parameters, so clients can see what is actually supported.
I tried mixing named params and catch-all params, but it doesn't recognize a handler:
myEndpoint(final #RequestParam String id, final #RequestParam String name, final #RequestParam Map<String, String> remainingParams)
Is it possible to map specific params and catch everything else in Map<String,String>? Or are there other possiblities, like mapping all params starting with filter using regex pattern?
Unfortunately I can't change source and target systems.
If your only concern with using a generic map is simply Swagger being accurate, why not just add the #ApiImplicitParams annotation to your endpoint? This would let you specify which params are required in your Swagger output:
#ApiImplicitParams(value = {
#ApiImplicitParam(name = "name", type = "String", required = true, paramType = "query"),
#ApiImplicitParam(name = "id", type = "String", required = true, paramType = "query")
})
Try
#RequestMapping
public String books(#RequestParam Map<String, String> requestParams, Other params)
{
//Your code here
}
You could make a class, e.g.
#Data
public class Paramss {
#NotNull
private String a;
private String b;
}
and then
#GetMapping
public Object params( #Valid #ModelAttribute Paramss params ) {
return params;
}
See also: Spring #RequestParam with class

How to get Form data as a Map in Spring MVC controller?

I have a complicated html form that dynamically created with java script.
I want to get the map of key-value pairs as a Map in java and store them.
here is my controller to get the submitted data.
#RequestMapping(value="/create", method=RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String createRole(Hashmap<String, Object) keyVals) {
....
}
but my map is empty.
How can i get form data as a map of name-value pairs in Spring mvc controller?
You can also use #RequestBody with MultiValueMap
e.g.
#RequestMapping(value="/create",
method=RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String createRole(#RequestBody MultiValueMap<String, String> formData){
// your code goes here
}
Now you can get parameter names and their values.
MultiValueMap is in Spring utils package
I,ve just found a solution
#RequestMapping(value="/create", method=RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String createRole(HttpServletRequest request) {
Map<String, String[]> parameterMap = request.getParameterMap();
...
}
this way i have a map of submitted parameters.
Try this,
#RequestMapping(value = "/create", method = RequestMethod.POST,
consumes = MediaType.APPLICATION_FORM_URLENCODED_VALUE)
public String createRole(#RequestParam HashMap<String, String> formData) {
The answers above already correctly point out that #RequestParam annotation is missing. Just to add why thta is required,
A simple GET request would be soemthing like :
http://localhost:8080/api/foos?id=abc
In controller, we need to map the function paramter to the parameter in the GET request. So we write the controller as
#GetMapping("/api/foos")
#ResponseBody
public String getFoos(#RequestParam String id) {
return "ID: " + id;
}
and add #RequestParam for the mapping of the paramter "id".

Spring RequestParam Map<String, String>

I have this in my controller:
#RequestMapping(value = "/myUrl", method = RequestMethod.GET)
public String myUrl(#RequestParam(value = "test") Map<String, String> test)
{
return test.toString();
}
And I'm making this HTTP request:
GET http://localhost:8080/myUrl?test[a]=1&test[b]=2
But in the logs I'm getting this error:
org.springframework.web.bind.MissingServletRequestParameterException: Required Map parameter 'test' is not present
How can I pass Map<String, String> to Spring?
May be it's a bit late but this can be made to work by declaring an intermediate class:
public static class AttributeMap {
private Map<String, String> attrs;
public Map<String, String> getAttrs() {
return attrs;
}
public void setAttrs(Map<String, String> attrs) {
this.attrs = attrs;
}
}
And using it as parameter type in method declaration (w/o #RequestParam):
#RequestMapping(value = "/myUrl", method = RequestMethod.GET)
public String myUrl(AttributeMap test)
Then with a request URL like this:
http://localhost:8080/myUrl?attrs[1]=b&attrs[222]=aaa
In test.attrs map all the attributes will present as expected.
It's not immediately clear what you are trying to do since test[a] and test[b] are completely unrelated query string parameters.
You can simply remove the value attribute of #RequestParam to have your Map parameter contain two entries, like so
{test[b]=2, test[a]=1}

Categories

Resources