Which method gets called for below request mapping? - spring

Among the below two methods, which one gets called first?
#RequestMapping(method = RequestMethod.POST, params="continue")
public String save(){
}
#RequestMapping(method = RequestMethod.POST, params="continuesave")
public String saveReview(){
}
Params sent in POST request include:
continue,
continuesave="true"
In my local machine, method 1 gets called. But in our prod servers, method 2 is getting called. What is the method calling criteria?

When I try to run your example, I get an exception java.lang.IllegalStateException: Ambiguous handler methods mapped
By the way, you can change the priority of handlers by negating params(saveReview won't call for both params):
#RequestMapping(method = RequestMethod.POST, params="continue")
public String save(){
...
}
#RequestMapping(method = RequestMethod.POST, params={"continuesave"," !continue"})
public String saveReview(){
...
}

You should only map to non overlapping urls. What happens in you case is just undefined behaviour: it may depends on many things and cannot be securely predicted (it even throws an exception in #Dekart test).
Here if both params can be simultaneously present in a request you should have only one mapping and test for the parameters inside the controller method:
#RequestMapping(method = RequestMethod.POST)
public String save_req(WebRequest web) {
Map<String,String[]> param = web.getParameterMap();
if (...) { // condition for save
return save();
}
else {
saveReview();
}
}
public String save(){
}
public String saveReview(){
}

Related

Access absolute path of resource when using #RequestMapping in spring boot controller

I am using #RquestMapping for mapping url to controller method.
#RestController
#RequestMapping(path = "/api/tasks")
public class TaskController { ....
and methods inside controller have /{id} with request mapping annotations.
#RequestMapping(
path = "/{taskId},
method = RequestMethod.GET
)
public Map<String, Object> methodOne(...
I want to access http method and absolute path (configured path) for that method inside.
i.e. I want to get /api/tasks/{taskId} as value (not the /api/tasks/1 if api is called for task id 1) and GET as method inside of the methodOne.
I checked the HandlerMapping but it returns actual path of resource called by client. Not the configured path for the method / resource.
Any help or guidance would be highly appreciated. Thank you.
String[] pathReqMappingAnnotationOnControllerClass = this.getClass().getAnnotation(RequestMapping.class).path();
Method method = TaskApiController.class.getMethod("getListOfTasks", HttpServletRequest.class, HttpServletResponse.class);
String[] pathReqMappingAnnotationOnControllerMethod = method.getAnnotation(RequestMapping.class).path();
String wholePath = pathReqMappingAnnotationOnControllerClass[0] + pathReqMappingAnnotationOnControllerMethod[0];
//pathReqMappingAnnotationOnControllerMethod will be empty array if method is not annotated for path
#RequestMapping(path = "/{id}", method = [RequestMethod.DELETE])
public void test(#PathVariable("id") String id, HttpServletRequest request) {
\\ Code Here
}
In the method parameter, id is the pathVariable. And request method is accessible in the request variable (Although I do not know what is the point as you are limiting it to only accept GET requests)
As suggested by #codedetector, best option is if you have request object or you can add one if you dont have it.
#RequestMapping(path = "/{taskId}, method = RequestMethod.GET)
public String methodOne(HttpServletRequest request){
String test = request.getRequestURI();
return test;
}
If you dont have request object in your method, with below code you can get any URL on your system.
import org.springframework.hateoas.mvc.ControllerLinkBuilder
...
ControllerLinkBuilder linkBuilder = ControllerLinkBuilder.linkTo(methodOn(YourController.class).getSomeEntityMethod(parameterId, parameterTwoId))
URI methodUri = linkBuilder.Uri()
String methodUrl = methodUri.getPath()
--------Edit
I am not sure why you need in this format "/api/tasks/{taskId}" as value (not the /api/tasks/1 )but i can think of using a constant use it for your #RequestMapping path parameter and then easily after getting absolute path , replace/append it with that constant.
String pathParam ="/{taskId}"
#RequestMapping(path = "/{id}", method = [RequestMethod.DELETE])
public void test(#PathVariable("id") String id, HttpServletRequest request) {
switch(id){
case 1:
method1();
break;
case 2:
method2();
break
....
....
}
}
private void method1(){};
private void method2(){};
private void method3(){};

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.

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.

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.

Spring REST multiple controllers for one URL but different http methods

I currently have one controller that handles both GET and POST for URL groups:
#Controller
public class RestGroups {
...
#RequestMapping(method = RequestMethod.GET, value = "/groups")
#ResponseBody
public GroupsDto groups() {
return new GroupsDto(getGroups());
}
#RequestMapping(method = RequestMethod.POST, value = "/groups", headers = "Accept=application/xml")
#ResponseBody
public GroupsDto postGroup(#RequestBody GroupDto groupDto) {
groupSaver.save(groupDto.createEntity());
return groups();
}
Now I would like to have TWO controllers, both assigned for same URL but each for different method, something like below:
#Controller
public class GetGroups {
...
#RequestMapping(method = RequestMethod.GET, value = "/groups")
#ResponseBody
public GroupsDto groups() {
return new GroupsDto(getGroups());
}
...
}
#Controller
public class PostGroup {
...
#RequestMapping(method = RequestMethod.POST, value = "/groups", headers = "Accept=application/xml")
#ResponseBody
public GroupsDto postGroup(#RequestBody GroupDto groupDto) {
groupSaver.save(groupDto.createEntity());
return groups();
}
...
}
Is it possible? Because now I get Spring exception that one URL cannot be handled by two different controllers. Is there a workaround for this issue? I really would like to separate those two completely different actions into two separate classes.
This limitation has been solved in Spring 3.1 with its new HandlerMethod abstraction. You'll have to upgrade to 3.1.M2. Let me know if you need an example.

Resources