REST API endpoint selection in Spring Boot - spring

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

Related

Spring throw Bad Request if the request param not available in Controller method

I want to restrict the list of allowed request parameters in my controller, so that if I get a request with an undefined parameter in the controller, it should return a bad request, but it returns 200 OK.
I think that this one should be fixed on the framework level, but not in the controller layer.
I am confused about it, and could not find anything on the internet
For e.g I have the following controller definition:
#GetMapping("/Client")
public ResponseEntity<List<Client>> getAllClients(HttpServletRequest httpServletRequest,
#RequestParam(value = McsConstants.PAGE_HEADER, required = false) Integer page,
#RequestParam(value = McsConstants.SIZE_HEADER, required = false) Integer size) {...}
And the valid request would be
GET .../Client
GET .../Client?page=1
GET .../Client?size=10
GET .../Client?page=1&size=10
How can I validate the case when I have an undefined request param like below?
GET .../Client?someUndefinedParam=1
Let me know please for any ideas or links
Thanks!
One way to handle this can be using #RequestParam annotation on a Map or MultiValueMap argument.
The map is populated with all the request parameters, therein you can write your logic to validate and reject unidentified query params.
EDIT: Following is an example for the same-
#RequestMapping(value = "/test", method = RequestMethod.GET)
public void testMethod(#RequestParam Map<String, String> allRequestParams) {
if (!isValidParams(allRequestParams)) {
// isValidParams() will validate the map with a reference map containing all the expected params
// throw BadRequestException here
}
}
Hope this helps!
let me share my knowledge it may helpful for some other scenarios
If the requestparam and variable is same name you no need to mention the value #RequestParam .
below i have mentioned with code example. Please share ur feedback
#GetMapping("/Client")
public ResponseEntity<List<Client>> getAllClients(HttpServletRequest httpServletRequest,
#RequestParam <Optional>Integer page,
#RequestParam <Optional>Integer size) {
//you can do ur logics
}

Issue with Spring Rest #RequestMapping when negating params

I have two spring controller methods :
#RequestMapping(value="/requestotp",method = RequestMethod.POST,params = "!applicationId") //new customer
public OTPResponseDTO requestOTP( #RequestBody CustomerDTO customerDTO){
return customerService.requestOTP(customerDTO);
}
#RequestMapping(value="/requestotp",method = RequestMethod.POST,params = {"idNumber","applicationId"}) //existing customer
public String requestOTP( #RequestParam(value="idNumber") String idNumber , #RequestParam(value="applicationId") String applicationId) {
return customerService.requestOTP(idNumber, applicationId);
}
using "!applicationId" , I am expecting that when I call the url with applicationId parameter there that the second method will be called , but actually when I pass a request like this :
{"idNumber":"345","applicationId":"64536"}
The first method gets called
This is the part of the params paremeters documentation that I rely on :
Finally, "!myParam" style expressions indicate that the specified
parameter is not supposed to be present in the request.
Can't you just simply delete first request params?
#RequestMapping(value="/requestotp",method = RequestMethod.POST) //new customer
public OTPResponseDTO requestOTP( #RequestBody CustomerDTO customerDTO){
return customerService.requestOTP(customerDTO);
}
The issue actually wasn't with negating the parameter, the issue was that I was sending {"idNumber":"345","applicationId":"64536"} in the POST body and I was expecting the variables to be mapped to the method parameters annotated with #RequestParam ... this is not correct ... #RequestParam only map URL parameters .... so the controller was trying to find the best match so it was using the first method as it contained #RequestBody

Spring rest use of pathVariable and RequestParam

With spring rest is there any reason to use request param?
For a search i don't know if i shoud use
#RequestMapping(value = "/lodgers/{searchParam}", method = RequestMethod.GET)
public Lodger getAllLogders(#PathVariable("searchParam") String searchParam)
or
#RequestMapping(value = "/lodgers/", method = RequestMethod.GET)
public Lodger getAllLogders(String searchParam)
As I use it, a path (pathVariables) points to a resource.
A queryParam (requestParam) results in a subset of a resource.
If you want certain users from /Users (e.g. beginning with "A", or lodgers named "Curt") this would be a subset, of all /Users and I see not a very good reason for having a special resource with that criteria, so I would use a queryParam here.

Spring URL mapping conflicts

At the moment I am bussy with implementing a new url structure for our webshop. The new url structure should be more optimized for search engines. We also want that our old structure will still be working and will use a 301 to redirect to a the new structure.
The problem is: the new structure sometimes conflicts with the old urls.
Example of the old url mapping:
#RequestMapping(value = "/brand/{categoryCode}/{categoryName}/{brandGroup}.do", method = RequestMethod.GET)
New structure:
#RequestMapping(value = "/brand/{brandGroup}/{superCategoryName}/{categoryName}.do", method = RequestMethod.GET)
As you can see the url's have the same amount of values, so the old mapping will catch the new one and vice versa.
What is the best way to fix this? Using a url filter to rewrite the old ones to the new url structure?
You could use an URL router in Spring MVC; you can define conflicting routes within your app and handle them with route prorities (first route to match the request wins) and refine request matching.
Your routes configuration file could look like:
GET /brand/{<[0-9]+>categoryCode}/{categoryName}/{brandGroup}.do oldcontroller.oldAction
GET /brand/{<[a-zA-Z]+>brandGroup}/{superCategoryName}/{categoryName}.do newController.newAction
In spring boot, regular expressions can be used when mapping the #PathVariable, and this can be useful to resolve url conflicts:
#RestController
public class TestController {
#PutMapping("/test/{id:^[1-9][0-9]*}") // id must be a number greater that 1
public void method1(#PathVariable long id, #RequestBody DataDto1 data) {
}
#PutMapping("/test/foo")
public void method1(#Valid #RequestBody DataDto2 data) {
}
}

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