Different RequestMapping value within same Controller - spring

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.

Related

Create generic method to forward to index.html requestes which throws 404

I Spring Boot app combined with Angular 4. I created some routes but when I type into url some string like localhost:8080/items I get error 404 not found. So that's why I created forwarding when it detected if it the url looks like localhost:8080/items it forwards to index.html. But what about a case when I have multiple possible urls to typed and I don't want to rewirte it again and again...?
#RequestMapping(value = "/api/user", method = RequestMethod.POST)
#RequestMapping(value = "/api/user", method = RequestMethod.GET)
#RequestMapping(value = "/api/item", method = RequestMethod.GET)
#RequestMapping(value = "/api/item", method = RequestMethod.POST)
.
.
.
and so on
I wanted to create something generic like:
#Controller
public class ViewController {
#RequestMapping({ "/**" })
public String index() {
return "forward:/index.html";
}
}
but it doesn't work at all. Or there is another way to make it work?
The proper correction of this case is:
In app.module add
{ path: '**', component: ItemListComponent}
which indicated what should you do when something goes wrong and add a specific java controller :
#Controller
public class ViewController {
#RequestMapping(value = "{path:[^\\.]*}")
public String index() {
return "forward:/index.html";
}
}

Spring Rest Controller find by id/ids methods

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.

Spring binding multiple attributes to same #ModelAttribute

I have a preview page which takes add or edit models and displays the preview.
#RequestMapping(value = "/preview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("editForm") FormModel editFormModel) {
//action
}
#RequestMapping(value = "/preview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("addForm") FormModel addFormModel) {
//action
}
I need to call preview from add form page and edit form page. The models I'm going to pass are same but come from different forms.
(1) Is there a way ModelAttribute supports this kind of multi-attribute name mapping?
(2) How can I think about redesigning this? Thinking about (a) Renaming the form name/attribute before form submit to use the same attribute name. (b) Remove ModelAttribute altogether - That's not an option for me as I'm using spring mvc form binding.
Note: I'm using editForm/addForm as session attributes.
Not actually solutions but work arounds.
Approach 1:
#RequestMapping(value = "/preview", method = RequestMethod.PUT)
public ModelAndView preview(#ModelAttribute("editForm") FormModel editFormModel) {
//action
}
#RequestMapping(value = "/preview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("addForm") FormModel addFormModel) {
//action
}
Approach 2:
#RequestMapping(value = "/editpreview", method = RequestMethod.PUT)
public ModelAndView preview(#ModelAttribute("editForm") FormModel editFormModel) {
//action
}
#RequestMapping(value = "/addpreview", method = RequestMethod.POST)
public ModelAndView preview(#ModelAttribute("addForm") FormModel addFormModel) {
//action
}

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.

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