Spring Rest Controller find by id/ids methods - spring

I have the following method in my Spring RestController:
#RequestMapping(value = "/{decisionId}", method = RequestMethod.GET)
public DecisionResponse findById(#PathVariable #NotNull #DecimalMin("0") Long decisionId) {
....
}
Right now I need to add the possibility to find a set of DecisionResponse by {decisionIds}.. something like this:
#RequestMapping(value = "/{decisionIds}", method = RequestMethod.GET)
public List<DecisionResponse> findByIds(#PathVariable #NotNull #DecimalMin("0") Set<Long> decisionIds) {
....
}
The following two methods don't work together.
What is a correct way of implementing this functionality? Should I leave only one method(second) that waits for {decisionIds} and returns a collection even when I need only 1 Decision object? Is there another proper way to implement this?

You can create a single endpoint for both sending a single long value as well as for the array of long values:
#RequestMapping(value = "/{decisionIds}", method = RequestMethod.GET)
public List<DecisionResponse> findByIds(#PathVariable #NotNull #DecimalMin("0") Set<Long> decisionIds) {
System.out.println(decisionIds);
}
And call this endpoint by sending path variable like this:
http://localhost:8080/11,12,113,14

Your problem is that you can't have 2 different methods on the same endpoint.
In other words, you can't have these two methods at the same time :
#RequestMapping(value = "/{decisionId}", method = RequestMethod.GET)
public DecisionResponse findById(#PathVariable #NotNull #DecimalMin("0") Long decisionId) {
....
}
#RequestMapping(value = "/{decisionIds}", method = RequestMethod.GET)
public List<DecisionResponse> findByIds(#PathVariable #NotNull #DecimalMin("0") Set<Long> decisionIds) {
....
}
Because
#RequestMapping(value = "/{decisionIds}", method = RequestMethod.GET)
And
#RequestMapping(value = "/{decisionId}", method = RequestMethod.GET)
Are the same endpoint.
So when you have HTTP request GET on http://<host>/19, you can't determine which method you want to use.
Solution
Rename your endpoints more clearly to avoid conflicts
#RequestMapping(value = "/decision/{Id}", method = RequestMethod.GET)
And
#RequestMapping(value = "/decisions/{Id}", method = RequestMethod.GET)
I hope this will help you.

Related

How to pass List<String> in post method using Spring MVC?

I need to pass a list of values in the request body of POST method but I get 400: Bad Request error.
Below is my sample code:
#RequestMapping(value = "/saveFruits", method = RequestMethod.POST,
consumes = "application/json")
#ResponseBody
public ResultObject saveFruits(#RequestBody List<String> fruits) {
...
}
The JSON I am using is: {"fruits":["apple","orange"]}
You are using wrong JSON. In this case you should use JSON that looks like this:
["orange", "apple"]
If you have to accept JSON in that form :
{"fruits":["apple","orange"]}
You'll have to create wrapper object:
public class FruitWrapper{
List<String> fruits;
//getter
//setter
}
and then your controller method should look like this:
#RequestMapping(value = "/saveFruits", method = RequestMethod.POST,
consumes = "application/json")
#ResponseBody
public ResultObject saveFruits(#RequestBody FruitWrapper fruits){
...
}
I had the same use case,
You can change your method defination in the following way :
#RequestMapping(value = "/saveFruits", method = RequestMethod.POST,
consumes = "application/json")
#ResponseBody
public ResultObject saveFruits(#RequestBody Map<String,List<String>> fruits) {
..
}
The only problem is it accepts any key in place of "fruits" but You can easily get rid of a wrapper if it is not big functionality.
You can pass input as ["apple","orange"]if you want to leave the method as it is.
It worked for me with a similar method signature.

Different RequestMapping value within same Controller

There is an existing Controller that I want to add an additional get Method with a slightly modified logic. There is the findAll method and I want to add the getMessages method.
#RestController
#RequestMapping(value = "/options", produces = MediaType.APPLICATION_JSON_VALUE)
public class OptionController {
...Definitions etc...
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<?> findAll(#PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
Page<Option> page = optionRepository.findAll(pageable);
return ok(pagingAssembler.toResource(page));
}
}
And below the new method:
#RequestMapping(value = "/optionsWelcome", produces = MediaType.APPLICATION_JSON_VALUE, method = RequestMethod.GET)
public ResponseEntity<?> getMessages(#PageableDefault(size = Integer.MAX_VALUE) Pageable pageable) {
Page<Option> page = optionRepository.findAll(pageable);
return ok(pagingAssembler.toResource(page));
}
I am getting 404 for http calls to /optionsWelcome but /options works.
Is it possible to have a controller with mappings for 2 different URLs or do I need to make a second controller?
/options is the mapping for the entire controller. /options/optionsWelcome will probably work.
You need to move /options mapping to the method.

Junit passing multiple parameters to rest service

I have a rest controller like bellow :
#RequestMapping(value = "/create", method = RequestMethod.POST)
public
#ResponseBody
GlobalResponse createDeal(#RequestBody Deal deal,#RequestBody Owner owner) {
// code here
}
I use Junit and Mockito for my test :
#Test
public void createDeal() throws Exception{
this.mockMvc.perform(post("/v1/Deal/create").content("\"deal\":{\"dealNumber\":\"DA001\"},\"owner\":{\"id\":1}").contentType(MediaType.APPLICATION_JSON)).andDo(print());
}
I cant past multiple parameters to the controller service , how can I avoid this ?
You won't be able to pass multiple arguments annotated with #RequestBody annotation. The argument annotated with this annotation holds the whole request body and it can't be split into multiple.
What you can do is to have a wrapper to hold your Deal and Owner objects and you can pass that wrapper as a single request body argument.
For e.g.:
public class Wrapper {
private Deal deal;
private Owner owner;
//Getters and setters
}
And your controller's method:
#RequestMapping(value = "/create", method = RequestMethod.POST)
public
#ResponseBody
GlobalResponse createDeal(#RequestBody Wrapper wrapper) {
// code here
}
Hope this makes sense.

Request parameters in spring

I need to take two parameters in my spring controller.
http://mydomain.com/myapp/getDetails?Id=13&subId=431
I have controller which will return Json for this request.
#RequestMapping(value = "/getDetails", method = RequestMethod.GET,params = "id,subId", produces="application/json")
#ResponseBody
public MyBean getsubIds(#RequestParam String id, #RequestParam String subId) {
return MyBean
}
I am getting 400 for when i tried to invoke the URL. Any thoughts on this?
I was able to get it with one parameter.
Try specifying which parameter in the query string should match the parameter in the method like so:
public MyBean getsubIds(#RequestParam("id") String id, #RequestParam("subId") String subId) {
If the code is being compiled without the parameter names, Spring can have trouble figuring out which is which.
As for me it works (by calling: http://www.example.com/getDetails?id=10&subId=15):
#RequestMapping(value = "/getDetails", method = RequestMethod.GET, produces="application/json")
#ResponseBody
public MyBean getsubIds(#RequestParam("id") String id, #RequestParam("subId") String subId) {
return new MyBean();
}
P.S. Assuming you have class MyBean.

Why Spring REST do not analyze "get","post" methods with same url

I am using spring rest , I have two methods
#RequestMapping(value="/",method = RequestMethod.POST)
public #ResponseBody
Object test1(HttpServletRequest request) {}
#RequestMapping(value="/",method = RequestMethod.GET)
public #ResponseBody
Object test2(HttpServletRequest request) {}
But it is not able to detect the two methods. Is it necessary that the URL should be different for each http method in spring.
Spring can support GET and POST for the same url. I've done it many times. For example (this is POST and PUT, but it's the same difference):
#Controller
#RequestMapping(value="player")
public class PlayerController {
#Autowired
private PlayerService playerService;
#RequestMapping(method = RequestMethod.POST)
#ResponseBody
public Player createNewPlayer(#RequestBody Player player) {
return playerService.createNewPlayer(player);
}
#RequestMapping(method = RequestMethod.PUT)
#ResponseBody
public Player updatePlayer(#RequestBody Player player) {
return playerService.updatePlayer(player);
}
}
If you can post the error message you're getting, maybe we can help you figure out what's wrong.
I am bit late but i might be useful for someone who still wants to know this concept. In below code we will be getting the error: java.lang.IllegalStateException: Ambiguous mapping.Cannot map 'XXX' method.
#RequestMapping(value="/",method = RequestMethod.POST)
public #ResponseBody
Object test1(HttpServletRequest request) {}
#RequestMapping(value="/",method = RequestMethod.GET)
public #ResponseBody
Object test2(HttpServletRequest request) {}
this error occurs because RequestHandlerMapper delegates the request on the basis of pattern of URL only and not on the method type.Hence if we have the same URL pattern, handlermapping will fail to distinguish that to which method it should map because of ambiguity.

Resources