#RequestParam and #PathVariable on the same variable in Spring Boot - spring-boot

In my Spring boot microservice I need to have dedicated endpoint that maps next urls
/delivery-options/myLabel
/delivery-options?label=myLabel
For handling both I try to use both #RequestParam and #PathVariable for my controller's method parameter but it does not work for both situations
#RequestMapping({"/delivery-options", "/delivery-options/{label}"})
public ResponseEntity<?> getDeliveryOptions(#RequestParam(value = "label", required = false) #PathVariable(value = "label", required = false) String label ) {
}
Is it possible to map both to one variable?

I believe we can only do this by assigning to two different variables and making both required to false.
I know this is not the solution you are looking for and also i believe the other way is declaring two different methods and mapping each request to one of them, even though that not makes the duplicate code because you are handling request and mapping to service layer based on input
#RequestMapping(value= {"/hello/{hi}","/hellodiff"}, method=RequestMethod.GET)
public void hello(#PathVariable(value="hi", required=false) String hi,
#RequestParam(value="key", required=false) String key) {
System.out.println("Output: "+ hi +" "+ key);
}
Output:
localhost:8080/hello/hey
Output: hey null
localhost:8080/hellodiff?key="cool"
Output: null "cool"

Related

How does field mapping happen for a PUT request body?

I am stuck with a basic confusion which I cannot test and cannot find a straightforward answer to as well.
I have an endpoint PUT which expects an object in the body , RequestObject.
It consists of 1 field only.
class RequestObject {
String name;
}
Now from the service from which I am hitting this endpoint, the object that I am supposed to use to send in the request has 2 fields.
class Test {
String firstname;
String age;
}
If I make a request, with age as null,
Will this work ?
Since firstName and name are not the same "spellings", will the mapping happen automatically ?
I am assuming No for both but I am not sure how to confirm.
WIll appreciate if someone can point me in right direction.
Thanks
#JsonIgnoreProperties(ignoreUnknown = true)
class RequestObject {
#JsonProperty("firstname")
String name;
}
By default Spring Boot uses the Jackson library to convert to objects. You can customize it using annotations. See https://github.com/FasterXML/jackson-annotations/wiki/Jackson-Annotations

REST API design - optional request parameters

I've written this request mapping to access a ticket by it's id:
#GetMapping(path = "/tickets/{ticketId}")
#ResponseStatus(value = HttpStatus.OK)
public ResponseEntity<List<TicketResponse>> getTicketsById(#PathVariable("ticketId") final Long ticketId
I'm planning to add multiple query parameters to support filtering such as ticketType, ticketStatus. REST API users should have options to filter on any or all of the query parameters.
What are the REST API design principles to achieve this ? Should I add new request parameters to support the filtering like below ? :
#GetMapping(path = "/tickets/{ticketId}")
#ResponseStatus(value = HttpStatus.OK)
public ResponseEntity<List<TicketResponse>> getTicketsById(#PathVariable("ticketId") final Long ticketId, #RequestParam("ticketType") final String ticketType, #RequestParam("ticketStatus") final String ticketStatus)
Is there a Spring design pattern for this scenario ? The Java builder pattern could be used where parameter an attribute of a QueryParameter object ?
You basically have two options:
Either you put all your RequestParams as method parameters, but with required=false, like #AmitKumar wrote. Example: #RequestParam(name="ticketType", required = false) String ticketType
Put all these parameters into a, let's say FilterDTO and have that as a parameter (FilterDTO filter). Spring will make sure to populate its fields with your RequestParams . Just put your ticketType and other parameters into the DTO as fields, and they will be optional. Example: public ResponseEntity<List<TicketResponse>> getTicketsById(#PathVariable("ticketId") ong ticketId, FilterDto filter){}
If you want to make parameter as an optional. you need to add required=false.
public ResponseEntity<List<TicketResponse>> getTicketsById(#PathVariable("ticketId") final Long ticketId, #RequestParam(name="ticketType", required=false) final String ticketType, #RequestParam(name="ticketStatus",required=false) final String ticketStatus)

Issue with Spring Rest #RequestMapping when negating params

I have two spring controller methods :
#RequestMapping(value="/requestotp",method = RequestMethod.POST,params = "!applicationId") //new customer
public OTPResponseDTO requestOTP( #RequestBody CustomerDTO customerDTO){
return customerService.requestOTP(customerDTO);
}
#RequestMapping(value="/requestotp",method = RequestMethod.POST,params = {"idNumber","applicationId"}) //existing customer
public String requestOTP( #RequestParam(value="idNumber") String idNumber , #RequestParam(value="applicationId") String applicationId) {
return customerService.requestOTP(idNumber, applicationId);
}
using "!applicationId" , I am expecting that when I call the url with applicationId parameter there that the second method will be called , but actually when I pass a request like this :
{"idNumber":"345","applicationId":"64536"}
The first method gets called
This is the part of the params paremeters documentation that I rely on :
Finally, "!myParam" style expressions indicate that the specified
parameter is not supposed to be present in the request.
Can't you just simply delete first request params?
#RequestMapping(value="/requestotp",method = RequestMethod.POST) //new customer
public OTPResponseDTO requestOTP( #RequestBody CustomerDTO customerDTO){
return customerService.requestOTP(customerDTO);
}
The issue actually wasn't with negating the parameter, the issue was that I was sending {"idNumber":"345","applicationId":"64536"} in the POST body and I was expecting the variables to be mapped to the method parameters annotated with #RequestParam ... this is not correct ... #RequestParam only map URL parameters .... so the controller was trying to find the best match so it was using the first method as it contained #RequestBody

how to capture multiple parameters using #RequestParam using spring mvc?

Suppose a hyperlink is clicked and an url is fired with the following parameter list myparam=myValue1&myparam=myValue2&myparam=myValue3 . Now how can I capture all the parameters using #RequestParam in spring mvc?
My requirement is I have to capture all the params and put them in a map.
Please help!
#RequestMapping(value = "users/newuser", method = RequestMethod.POST)
public String saveUser(#RequestParam Map<String,String> requestParams) throws Exception{
String userName=requestParams.get("email");
String password=requestParams.get("password");
//perform DB operations
return "profile";
}
You could use RequestParam in the above mentioned manner.
It seems you can't get
Map<String,String>
because all your params have same name "myparam"
Try this instead:
public ModelAndView method(#RequestParam("myparam") List<String> params) { }
To get all parameters at once try this:
public ModelAndView postResultPage(#RequestParam MultiValueMap<String, String> params)
This feature is described in the #RequestParam java doc (3. Paragraph):
Annotation which indicates that a method parameter should be bound to a web request parameter. Supported for annotated handler methods in Servlet and Portlet environments.
If the method parameter type is Map and a request parameter name is specified, then the request parameter value is converted to a Map assuming an appropriate conversion strategy is available.
If the method parameter is Map<String, String> or MultiValueMap<String, String> and a parameter name is not specified, then the map parameter is populated with all request parameter names and values.
As of Spring 3.0, you can also use MultiValueMap to achieve this:
A rudimentary example would be:
public String someMethod(#RequestParam MultiValueMap<String,String> params) {
final Iterator<Entry<String, List<String>>> it = params.entrySet().iterator();
while(it.hasNext()) {
final String k = it.next().getKey();
final List<String> values = it.next().getValue();
}
return "dummy_response";
}
If anyone is trying to do the same in Spring Boot, use RequestBody in place of RequestParam
Spring mvc can support List<Object>, Set<Object> and Map<Object> param, but without #RequestParam.
Take List<Object> as example, if your object is User.java, and it like this:
public class User {
private String name;
private int age;
// getter and setter
}
And you want pass a param of List<User>, you can use url like this
http://127.0.0.1:8080/list?users[0].name=Alice&users[0].age=26&users[1].name=Bob&users[1].age=16
Remember to encode the url, the url after encoded is like this:
http://127.0.0.1:8080/list?users%5B0%5D.name=Alice&users%5B0%5D.age=26&users%5B1%5D.name=Bob&users%5B1%5D.age=16
Example of List<Object>, Set<Object> and Map<Object> is displayed in my github.
You can use for multiple Params as such
public String saveUser(#RequestParam("email") String userName, #RequestParam("password") String password) throws Exception{
//your code
//perform DB operations
return "profile";
}
For params with same name, you can use MultiValueMap<String ,String>. Then all the values would be present as List
You can use multiple #RequestParam annotations as shown below.
#RequestParam(value="myparam1", required = true) <Datatype> myparam1,
#RequestParam(value = "myparam2", required = false) <Datatype> myparam2,

Spring MVC: how to indicate whether a path variable is required or not?

I am doing a Spring web. For a controller method, I am able to use RequestParam to indicate whether a parameter it is required or not. For example:
#RequestMapping({"customer"})
public String surveys(HttpServletRequest request,
#RequestParam(value="id", required = false) Long id,
Map<String, Object> map)
I would like to use PathVariable such as the following:
#RequestMapping({"customer/{id}"})
public String surveys(HttpServletRequest request,
#PathVariable("id") Long id,
Map<String, Object> map)
How can I indicate whether a path variable is required or not? I need to make it optional because when creating a new object, there is no associated ID available until it is saved.
Thanks for help!
VTTom`s solution is right, just change "value" variable to array and list all url possibilities: value={"/", "/{id}"}
#RequestMapping(method=GET, value={"/", "/{id}"})
public void get(#PathVariable Optional<Integer> id) {
if (id.isPresent()) {
id.get() //returns the id
}
}
There's no way to make it optional, but you can create two methods with one having the #RequestMapping({"customer"}) annotation and the other having #RequestMapping({"customer/{id}"}) and then act accordingly in each.
I know this is an old question, but searching for "optional path variable" puts this answer high so i thought it would be worth pointing out that since Spring 4.1 using Java 1.8 this is possible using the java.util.Optional class.
an example would be (note the value must list all the potential routes that needs to match, ie. with the id path variable and without. Props to #martin-cmarko for pointing that out)
#RequestMapping(method=GET, value={"/", "/{id}"})
public void get(#PathVariable Optional<Integer> id) {
if (id.isPresent()) {
id.get() //returns the id
}
}
VTToms answer will not work as without id in path it will not be matched (i.e will not find corresponding HandlerMapping) and consequently controller will not be hit. Rather you can do -
#RequestMapping({"customer/{id}","customer"})
public String surveys(HttpServletRequest request, #PathVariable Map<String, String> pathVariablesMap, Map<String, Object> map) {
if (pathVariablesMap.containsKey("id")) {
//corresponds to path "customer/{id}"
}
else {
//corresponds to path "customer"
}
}
You can also use java.util.Optional which others have mentioned but it requires requires Spring 4.1+ and Java 1.8..
There is a problem with using 'Optional'(#PathVariable Optional id) or Map (#PathVariable Map pathVariables) in that if you then try to create a HATEOAS link by calling the controller method it will fail because Spring-hateoas seems to be pre java8 and has no support for 'Optional'. It also fails to call any method with #PathVariable Map annotation.
Here is an example that demonstrates the failure of Map
#RequestMapping(value={"/subs","/masterclient/{masterclient}/subs"}, method = RequestMethod.GET)
public List<Jobs> getJobListTest(
#PathVariable Map<String, String> pathVariables,
#RequestParam(value="count", required = false, defaultValue = defaultCount) int count)
{
if (pathVariables.containsKey("masterclient"))
{
System.out.println("Master Client = " + pathVariables.get("masterclient"));
}
else
{
System.out.println("No Master Client");
}
//Add a Link to the self here.
List list = new ArrayList<Jobs>();
list.add(linkTo(methodOn(ControllerJobs.class).getJobListTest(pathVariables, count)).withSelfRel());
return list;
}
I know this is an old question, but as none of the answers provide some updated information and as I was passing by this, I would like to add my contribution:
Since Spring MVC 4.3.3 introduced Web Improvements,
#PathVariable(required = false) //true is default value
is legal and possible.
#RequestMapping(path = {"/customer", "/customer/{id}"})
public String getCustomerById(#PathVariable("id") Optional<Long> id)
throws RecordNotFoundException
{
if(id.isPresent()) {
//get specific customer
} else {
//get all customer or any thing you want
}
}
Now all URLs are mapped and will work.
/customer/123
/customer/1000
/customer - WORKS NOW !!

Resources