Difference between the property param of #requestMapping and #requestParam [duplicate] - spring

This question already has answers here:
diffrence between #RequestParam and #RequestMapping
(2 answers)
Closed 2 years ago.
I found that both of the property param of #requestMapping and #requestParam could realize binding method parameter to URL address ? So in general case can they be replaced by each other ?

probably you should have elaborated your question with examples.
Anyhow, #RequestMapping params and #RequestParams are generally used for different purposes
Let's take the example of:https://stackoverflow.com/ questions/63871895
In this URI we can see two different handlers in a controller.
#RequestMapping(path = "/questions/{id}", method = RequestMethod.GET)
public Question getQuestion(#PathVariable int id) {
// returns a particular question
}
#RequestMapping(path = "/questions", method = RequestMethod.GET)
public List<Question> getQuestions() {
// returns all questions
}
Now here, Parameter mappings are considered as restrictions that are enforced. The primary path mapping (i.e. the specified URI value) still has to uniquely identify the target handler, with parameter mappings simply expressing preconditions for invoking the handler.
Now, let's see an example for #RequestParams: https://www.google.com/search?client=opera&q=stackoverflow&sourceid=opera&ie=UTF-8&oe=UTF-8
In this URL we can see one handler as:
#RequestMapping(path = "/search", method = RequestMethod.GET)
public List<Results> getResults(#RequestParam Map allRequestParams) {
// returns results based on query parameters
}
This will always call the same handler whether query params are provided or not. So #RequestParams is used to extract the query parameters from the URL.
So, generally, you can try to use #RequestMapping params in place of #RequestParams but it will have the effects explained in the above examples.
sources: https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestMapping.html
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/bind/annotation/RequestParam.html

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
}

Access function parameter by annotation in AOP (AspectJ)

I have a function under class MyController:
#RestController
#RequestMapping(value = "/api/service")
public class MyController {
#PostMapping(value = "add_person")
public MyResponse addPerson(#RequestBody Person person) {
// ...
}
#PostMapping(value = "add_person_2")
public MyResponse addPerson(#PathVariable(value = "person_age") Int age, #RequestBody Person person) {
// ...
}
}
I have setup AspectJ in my project to have a AOP logic to run whenever those two addPerson(...) method above is called:
#Around("execution(public MyResponse addPerson(..))")
public void around(ProceedingJoinPoint joinPoint) {
// NO matter which addPerson(...) is executing, I am only interested in the
// parameter value annotated with #RequestBody.
// How can I access the parameter that passed in addPerson(...) & is annotated with
// #RequestBody through ProceedingJoinPoint ?
}
My question is mentioned in above code comment. I wonder how can I access the parameter annotated with #RequestBody in my AOP function? I don't want to check parameter type or name, but interested to know how to access parameter by checking the annotation through ProceedingJoinPoint. Is it possible?
I do not want to mark this question as a duplicate because it is no exact duplicate, but my answer here should answer the question about how to
match an annotated parameter at any position,
get the annotation + the parameter value itself.
The linked answer uses a #Before advice. If you want to somehow replace the value by another one in an #Around advice when calling proceed() this is also possible, but was not asked here and my request for seeing more of the advice method body was also ignored.
If you want to limit to annotated Person parameters, you would have to use the fully qualified class name my.package.Person instead of the * inside (*) and do the corresponding cast after accessing the parameter in the advice body.
In my comment I also asked if the parameter has a fixed relative position in the parameter list such as first, last or second/third from left/right. If the OP would have confirmed such a fixed relative position, reflection would not be necessary and the corresponding parameter could be bound to an advice method parameter directly via args() pointcut designator. This would be quite elegant and eliminate the need to loop over getArgs() or over a two-dimensional array of parameter annotations.

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

Differentiating url with Path parameters

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

Resources