Bind #PostMapping to multiple parameters instead of model bean? - java

When using #GetMapping, I could bind each get-query parameter to one method parameter with #RequestParam annotation.
The following does not work, it would only be valid with #GetMapping:
//#PostMapping("/search")
#GetMapping("/search")
public void search(#RequestParam String origin, #RequestParam destination) {
}
Question: how can I achieve the same with #PostMapping?
Or do I always have to use a model bean like:
#PostMapping("/search")
public void search(#RequestBody model) {
}

The two ways are different, if the payload contains an object representing a serializable entity you should go for the second way and let jackson handle the deserialization for you, if not you can use the first one or you can build an entity for that , both works

Related

Having alias param names to accept Url Encoded Form data values

In my Spring web application, I have an API that accepts requests with application/x-www-form-urlencoded content type.
#RequestMapping(value = "/do-it", method = {RequestMethod.POST})
public String test(#ModelAttribute("request")RequestDTO request,HttpServletRequest
httpServletRequest, Map<String, Object> model, RedirectAttributes redirectAttributes){
.....
}
My RequestDTO has following fields in it.
public class RequestDTO {
private String paramOne;
private String paramTwo;
// standard getters and setters
}
This implementation works fine, all the request params get mapped to the request dto as expected. However, now I have this requirement to accept the requests with the fields in following pattern.
param_one, param_two
I understand that, using #JsonProperty annotation on the fields in my request dto is not gonna work in my case since the request is not in the type of application/json.
The only way I have found to solve the issue is creating new setters like following (which looks ugly in my opinion when it comes to naming convention).
public void setParam_one(String param_one) {
this.paramOne = param_one;
}
Can some one help me to find a better way to get this done? I cannot change the param names in original request dto.
Thank you..!
I was able to get this done. Thanks to #neetash for guiding me.
Everything I needed to have was a Custom HandlerMethodArgumentResolver to map the post request body data to the object that I wanted to get.
I followed following linked tutorial to implement it. It contains every single thing someone needs to know to create a HandlerMethodArgumentResolver.
https://www.petrikainulainen.net/programming/spring-framework/spring-from-the-trenches-creating-a-custom-handlermethodargumentresolver/

Is there a way to have the same mapping for different methods with different RequestBodies? (PATCH-Request)

I have the following two API-methods:
#PatchMapping("/{id}")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public ResponseEntity<Project> updateProjectInactivity(#PathVariable long id, #RequestBody InactivityDTO inactivityDTO)
throws ProjectNotFoundException {
return projectService.updateProjectInactivity(id, inactivityDTO);
}
#PatchMapping("/{id}")
#PreAuthorize("hasRole('ROLE_ADMIN')")
public ResponseEntity<Project> updateProjectStatus(#PathVariable long id, #RequestBody StatusDTO statusDTO)
throws ProjectNotFoundException {
return projectService.updateProjectStatus(id, statusDTO);
}
The two methods have a different #RequestBody, but im currently getting an error because both of them have the same mapping.
Is there a way to have the same mapping for different methods with different RequestBodies? If not, whats the best workaround solution to achieve what i want? I could think of giving them a different #RequestParameter, but that would be ugly, because i dont need that parameter. It would be only used to achieve different mapping.
It is because of the #PatchMapping("/{id}") in both methods.
You can have the same url with different request mappings. like below
#DeleteMapping("/{id}")
#PatchMapping("/{id}")
You should start using different request path since both the methods are responsible for different actions or changing different sub domain. (As per rest convention)
for editing the status
#PostMapping({"{id}/status"}) - It represents that you are editing the status of an Object.
similarly, you should use different request path for inactivity

Spring MVC: use different JSR-303 validators on the same bean?

I'm using Constraint Validators (JSR-303) on my beans in a Spring MVC Controller. Given the following java bean:
public class EntityDTO {
protected Long id;
protected Integer version;
protected String someOtherField;
// getters and setters
}
I have 2 different types of constraint validation I would like to perform. Either both id and version are null, or both are non-null.
I can create 2 different constraint validators and assign to 2 different annotations: DTOEntityFieldsEmpty & DTOEntityFieldsNotEmpty. But then I have to specify the validator annotation at the bean level.
#DTOEntityFieldsEmpty
public class EntityDTO {
....
}
However, I'm looking to specify the validator that I want to use in the actual controller method level. Under normal circumstances, my method would be:
public void updateData( #RequestBody #Valid EntityDTO dto){
...
}
where the #Valid annotation will apply the Validator that is defined in the EntityDTO object. But I'm looking to see if there is a way I can either pass a parameter at the #Valid request, or specify the validator to use.
// #Valid annotation not supported this way
public void updateData( #RequestBody #Valid(validator=DTOEntityFieldsEmpty.class) EntityDTO dto){
...
}
Is there anything I can do to get around this? I realize that I can use Spring's #InitBinder, but that will bind a validator to the entire Controller, and not just one specific method.
I've checked both JSR-303 1.0 and 1.1, and don't see anything that jumps out at me to handle this circumstance. Similarly, I can't find anything in the Spring 3 or 4 docs either. I wonder if there might be a way using Group validation, but not entirely sure. I would need to be able to know which validator was successful or failed in the controller, and that seems a little hacky to me.
You can use Validation Groupes. Therefore you need to assign the constraints at the fields to groups and then you need to tell spring which group to validate (this can be one or more groupes), therefore Spring 3.1 introduced the #Validated annotation.
public interface GroupA { } //empty marker interface
public interface GroupB { }
#DTOEntityFieldsEmpty(groups=GroupA.class)
#DTOEntityFieldsNotEmpty(groups=GroupB.class)
public class EntityDTO {
....
}
public void updateData(
#RequestBody #Validated({ GroupA.class }) EntityDTO dto){
...
}

Can I use JSON data in RequestMapping in Spring MVC

I'm thinking the answer here is probably no, but just in case.
I'm doing something like this:
#RequestMapping(value="data.json", params="query=overview")
public String getOverview(#RequestBody MyRequest myRequest) {
[...]
return "overview";
}
#RequestMapping(value="data.json", params="query=detail")
public String getDetail(#RequestBody MyRequest myRequest) {
[...]
return "detail";
}
and the client is POSTing JSON data, which is deserialized by Jackson on the way in and bound to the MyRequest parameter, all working nicely.
However, I don't like the query type having to be specified in the URL of the requests. What I would like is to include the query parameter in the JSON object and use that to drive the #RequestMapping. Is this possible?
If not, I guess I will implement a single mapping and have that method delegate to others based on the incoming data, but that feels like a step in the wrong direction.
What you are trying to do does not work out of the box.
If you don't like the param why don't you add the qualifier to the URL like so:
#RequestMapping("/overview/data.json")
#RequestMapping("/detail/data.json")
If you absolutely need the functionality you mention, you could implement a custom RequestMappingHandlerMapping that would do what you want by extending that class as is done here.
It's not possible if you remove the params. You have to have something distinct between the two mappings. If you are intent on getting rid of the params, best you could do is have a single method/mapping and call your services or whatever other logic you have according to what the value of query is in your MyRequest object.
#RequestMapping(value="data.json")
public String getOverviewOrDetail(#RequestBody MyRequest myRequest) {
if (myRequest.getQuery().equalsIgnoreCase("overview")) {
[...]
return "overview"
} else if(myRequest.getQuery().equalsIgnoreCase("detail")) {
[...]
return "detail"
}
}
Since both methods are unmarshalling to the same object, you don't really need two separate methods/mappings.

Spring Web MVC: Use same request mapping for request parameter and path variable

Is there a way to express that my Spring Web MVC controller method should be matched either by a request handing in a ID as part of the URI path ...
#RequestMapping(method=RequestMethod.GET, value="campaigns/{id}")
public String getCampaignDetails(Model model, #PathVariable("id") Long id) {
... or if the client sends in the ID as a HTTP request parameter in the style ...
#RequestMapping(method=RequestMethod.GET, value="campaigns")
public String getCampaignDetails(Model model, #RequestParam("id") Long id) {
This seems to me a quite common real-world URL scheme where I don't want to add duplicate code, but I wasn't able to find an answer yet. Any advice highly welcome.
EDIT: It turns out that there seems currently (with Spring MVC <= 3.0) no way to achieve this, see discussion inside Javi's answer.
You can set both mapping url for the same function and setting id as optional.
#RequestMapping(method=RequestMethod.GET, value={"/campaigns","/campaigns/{id}"})
public String getCampaignDetails(Model model,
#RequestParam(value="id", required=false) Long id,
#PathVariable("id") Long id2)
{
}
though it would map as well when id is not sent, but you can control this inside the method.
EDIT: The previous solution doesn't work because #PathVariable is not set to null when there isn't {null} and it cannot map the URL (thanks ngeek). I think then that the only possible solution is to create two methods each one mapped with its #MappingRequest and inside one of them call the other function or redirect to the other URL (redirect: or forward: Spring prefixes). I know this solution is not what you're looking for but think it's the best you can do. Indeed you're not duplicating code but you're creating another function to handle another URL.
If you still want to stick to PathVariable approach and if you are getting 400 syntactically incorrect error then follow this approach-
#RequestMapping(method=RequestMethod.GET, value={"campaigns/{id}","campaigns"})
public String getCampaignDetails(Model model,
#PathVariable Map<String, String> pathVariables)
{
System.out.println(pathVariables.get("id"));
}
The #RequestMapping annotation now supports setting the path attribute instead of name or value. With path, you can achieve the mapping desired by this question:
#RequestMapping(method=RequestMethod.GET, path="campaigns/{id}")
public String getCampaignDetails(Model model, #PathVariable("id") Long id) {
#RequestMapping(method=RequestMethod.GET, value="campaigns")
public String getCampaignDetails(Model model, #RequestParam("id") Long id) {

Categories