Differentiating url with Path parameters - spring

We are working on a REST based service and we are using Spring MVC for the same. We are facing a url to method resolution problem. This is roughly what we are trying to do
Suppose we have persons with their pets
//Class level request mapping
#RequestMapping("/persons")
// Mapping to access a specific person inside one of the methods
#RequestMapping(value="/{personId}", method= RequestMethod.GET
//.... getPerson method
// Mapping to access a specific person's pets inside one of the methods
#RequestMapping(value="/{personId}/pets", method= RequestMethod.GET
// getPersonPets method
If the request comes as "/persons/P12323233" where P12323233 is person id, it resolves to the getPerson method.
If the request comes as "/persons/P12323233/pets" where P12323233 is person id, it resolves to the getPersonPets method.
So everything is fine till now. But
If the request comes as "/persons/pets, the request resolves to the getPerson method. While we can handle this as an error case inside in the getPerson method, we are trying to check whether there is anyway of resolving this call to getPersonPets method.
We are still debating whether the right place to handle this case is getPerson or getPersonPets method. That debate aside, we wanted to know whether it is even technical feasible to achieve resolution to the getPersonPets method.
Appreciate any help.

You could also use regex and filter out such request as 404. For example:
// Mapping to access a specific person inside one of the methods
#RequestMapping(value="/{personId:P[\\d]+}", method= RequestMethod.GET
//.... getPerson method
// Mapping to access a specific person's pets inside one of the methods
#RequestMapping(value="/{personId:P[\\d]+}/pets", method= RequestMethod.GET
// getPersonPets method

This can be solved by adding multiple mappings for the same method:
#RequestMapping(value="/{personId}", method= RequestMethod.GET
//.... getPerson method
#RequestMapping(value = {"/{personId}/pets", "/pets"}, method= RequestMethod.GET
// getPersonPets method
Update:
Notice that using the following signature will throw an exception when accessing /pets as the personId is not present in the URL:
public String showPets(#PathVariable("personId") long personId)
This would be the most convenient way to access the variable, but given we have multiple paths mapped the same method, we could change the signature to the following in order to avoid getting the exception:
public String showPets(#PathVariable Map<String, String> vars)
And retrieve the path variable from the map

Although generally REST assumes you are referencing an ID when you are not specifying the segment on the resource, I like adding id, to make the rest endpoint less ambiguous.
Consider doing the following if you are allowed to change the API:
//Class level request mapping
#RequestMapping("/persons")
// Mapping to access a specific person inside one of the methods
#RequestMapping(value="/id/{personId}", method= RequestMethod.GET
//.... getPerson method
// Mapping to access a specific person's pets inside one of the methods
#RequestMapping(value="/id/{personId}/pets", method= RequestMethod.GET
// getPersonPets method

Related

#ModelAttribute vs #RequestBody, #ResponseBody

#ModelAttribute
RequestMapping(value="/owners/{ownerId}/pets/{petId}/edit", method =
RequestMethod.POST)
public String processSubmit(#ModelAttribute Pet pet) { }
http://.../?name=Something&age=100
public String doSomething(#ModelAttribute User user) { }
#RequestBody
#RequestMapping(value = "/user/savecontact", method = RequestMethod.POST
public String saveContact(#RequestBody Contact contact){ }
{ "name": "Something", "age": "100" } in request body
public String doSomething(#RequestBodyUser user) { }
#ModelAttribute will take a query string. so, all the data are being pass to the server through the url
#RequestBody, all the data will be pass to the server through a full JSON body
Now which one is the best approach ???
If both are for same purpose to bind to the bean..which one is the best practice or widely used as standard practice?
Both handles multi-part file and does it both have equivalent options with one another ?
https://javabeat.net/spring-multipart-file-upload/
How do i upload/stream large images using Spring 3.2 spring-mvc in a restful way
Does any one of them has lesser capabilities then the other one? Like length limitations, method limitations. Drawbacks
Which one is more secured in terms of security ?
As the javadoc suggests, it's the usage that sets them apart, i.e., use #ModelAttribute if you want to bind the object back to the web view, if this is not needed, use #RequestBody
#RequestBody
Usecases : Restful controllers (ex: produce and consume json/xml, processing direct document download requests, searching for stuff, ajax requests )
As the name suggests the if a method argument is annotated with #RequestBody annotation Spring converts the HTTP request body to the Java type of the method argument.
Is only allowed on method parameters (#Target(value={ PARAMETER}))
The body of the request is passed through an HttpMessageConverter to resolve the method argument depending on the content type of the request.
works for Post and not Get method.
#ModelAttribute
Usecases : web app controllers (ex: binding request query parameters, populating web views with options and defaults)
Uses data binders & ConversionService
Is allowed on methods and method params(#Target(value={METHOD, PARAMETER}))
Useful when dealing with model attributes for adding and retrieving model attributes to and from the Srping’s Model object
When used on METHODS, those methods are invoked before the controller methods annotated with #RequestMapping are invoked
binds a method PARAMETER or method return value to a named model attribute & the bound named model attributes are exposed to a web view
binds request query parameters to bean
For more information on Data Binding, and Type Conversion refer: https://docs.spring.io/spring/docs/5.1.x/spring-framework-reference/core.html#validation

how to apply post method to all request mappings in spring

How i can access to request POST data from different url-s to one controller method, for example I have /countries & /countries/{id} URL, It works very good with first one, because my code is
#RequestMapping(value = {"/{id}", "/{id}/"}, method = RequestMethod.GET)
public String getCountry(#PathVariable(value = "id", required = true) int id,ModelMap model) {
}
#RequestMapping(method = RequestMethod.POST)
public String deleteCountry(ModelMap model,HttpServletRequest request) {
}
And when I try to request POST data from second url I'm getting
HTTP Status 405 - Request method 'POST' not supported
Which is expectable because I haven't POST method with this mapping, but if I will be made one method for one mapping my code will be too ugly ant terrible, what solution I can use?
Hum why not add the "array" of value to your second method as well ?
#RequestMapping(value = {"", "/{id}"},method = RequestMethod.POST)
public String deleteCountry(ModelMap model,
HttpServletRequest request) {
Btw using POST verb to call an action that looks like it will delete the resource seems wrong, it should be a DELETE verb used
EDIT
But in reality, you should be creating 2 methods, as both those method are not supposed to do the same thing.
POST /countries should be creating country resources
POST /countries/{id} should be doing something else.
For an update prefer PUT /countries/{id}
And for a delete, prefer DELETE /countries/{id}
There is one way to have separate handler interceptors for different controllers.
Refer this link for details.
bind Spring HandlerInterceptor only to one controller
But I feel, it may be good you can create a common method to share business logic for this.
As Interceptor comes with proxy class for your controller which can be avoided unless you have complex logic.

REST API endpoint selection in Spring Boot

Given a controller like this:
#RestController
#RequestMapping("/cars") {
public class CarController{
#RequestMapping(method = RequestMethod.GET)
public ResponseEntity<List<Cars>> getCars() { //logic }
#RequestMapping(method = RequestMethod.GET")
public ResponseEntity<List<Cars>> searchCar(#RequestParam("name") String name, #RequestParam("value") String value) { //logic}
}
If the url is like this localhost/cars I would like to access the getCars() method.
But if the url is :
localhost/cars?name=something&value=100 or
localhost/cars?name=something or
localhost/cars?value=100
I would like the second method to be accessed.
Is this possible to do?
You are still asking for the same list of resources, cars, only thing is that you are adding a filter or search / query criteria.
It would be beneficial to develop a query filter / criteria to support something like:
/cars?q=make+eq+acura (meaning make=acura)
/cars?q=price+lt+25000 (meaning price <25000)
and so on.
No it is not possible. because when a request comes to container then it will 1st scan all the URL and check uniqueness of the URL. If there is duplicate URL present then container will throws exception.
In your case you are using class level URL mapping, but you are not using method level URL mapping.
To access your getCars() method you need to use some URL like below
#RequestMapping(value = "/", method = RequestMethod.GET)
To access your 2nd method you need to use another mapping URL
#RequestMapping(values="/test", method = RequestMethod.GET")
You can't access
localhost/cars?name=something&value=100 or
localhost/cars?name=something or
localhost/cars?value=100
as you are using 2 parameters like #RequestParam("name") String name, #RequestParam("value") String value
you need to pass two parameter in your url like below
localhost/cars/test?name=something&value=100
if you don't want to pass any of two parameter then just pass it as null and check it inside your method

Spring Data Rest custom controller with patch method - how to merge resource with entity

By default when we have a repository with save method exposed we can do a PATCH request. Then Spring Data REST retrieves the original object from the database and apply changes to entity and then saves it for us (inside JsonPatchHandler class). This allows us to do the following request for class
class Address {
Long id;
String street;
Long houseNumber;
}
PATCH /api/addresses/1 with body
{ houseNumber: 123 }
And only this one field will be changed.
Now having custom controller we would like to in the update method receive the whole object (after HATEOAS merged it with the original object from the DB)
#RepositoryRestController
#ExposesResourceFor(Address.class)
#ResponseBody
#RequestMapping("/addresses")
public class AdddressController {
#PatchMapping("/{addressId}")
public Resource<Address> update(#RequestBody Resource<Address> addressResource, #PathVariable Long addressId) {
Address address= addressResource.getContent();
// .... some logic
address = addressRepository.save(address);
return new Resource<>(address);
}
}
Unfortunately in the place where I would do some logic I get the Address with null fields instead of the merged object.
Is it possible to plug the custom controller in the Spring Data REST stack so that when patching the request it will merge it for me (as it does for normal repositories)?
edit:
I would like to find a solution that works transparently both with PATCH(content-type:application/json-patch+json) and PATCH(content-type: application/hal+json)
After browsing the Spring sources I haven't found a reasonable solution. As a result I've created issue in their - JIRA
For the moment the only reasonable workaround is following - create custom controller that has PersitentEntityResource as a parameter and has both {id} and {repository} placeholders in its path i.e.
#PatchMapping("/addresses/{id}/{repository}")
public Resource<Address> update(PersistentEntityResource addressResource) {
...
}
which makes the invocation endpoint /addresses/123/addresses

Mapping same URL to different controllers in spring based on query parameter

I'm using spring annotation based controller. I want my URL /user/messages to map to some controller a if query parameter tag is present otherwise to some different controller b. This is required because when parameter tag is present then some more parameters can be present along with that which i want to handle in different controller to keep the implementation clean.Is there any way to do this in spring. Also is there any other elegant solution to this problem ?
You can use the params attribute of the #RequestMapping annotation to select an controller method depending on Http parameters.
See this example:
#RequestMapping(params = "form", method = RequestMethod.GET)
public ModelAndView createForm() {
...
}
#RequestMapping(method = RequestMethod.GET)
public ModelAndView list() {
...
}
It is a REST style like Spring ROO uses: if the request contains the parameter forms then the createForm handler is used, if not the list method is used.
If you want to go the Spring route, you can checkout the HandlerInterceptor mentioned here. The Interceptor can take a look at your query param and redirect the link to something else that can be caught by another SimpleUrlMapper.
The other way is to send it to a single controller and let the controller forward to another action if the query parameter is "b".

Resources