how to apply post method to all request mappings in spring - 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.

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
}

#RequestMapping(params={"myyParam"}) seems to be getting ignored?

I am redirecting to page using basic javascript:
window.location = "handleMyPage.ctl?myNewParameter=1234";
however when the flow of control (correctly) gets to my controller, the request is instead handled by a different (incorrect) function.
My function is annotated like:
#RequestMapping(method = { RequestMethod.GET, RequestMethod.POST }, params={"somePreExisingParameterIAmNotUsing"})
protected ModelAndView otherHandler(HttpServletRequest request, HttpServletResponse response) {
log.info("This gets called for some reason!");
}
#RequestMapping(method = { RequestMethod.GET, RequestMethod.POST }, params={"myNewParameter"})
protected ModelAndView handlerIActuallyWantCalled(HttpServletRequest request, HttpServletResponse response) {
log.info("this never gets called?");
}
I have inspected the request at the server side - and I can see the parameterMap contains only "myNewParameter" and no trace of "somePreExisingParameterIAmNotUsing" - yet still the function which references somePreExisingParameterIAmNotUsing is the only one that ever gets called.
it's worth noting, somePreExisingParameterIAmNotUsing is also the first function referenced - so I believe the request dispatcher may just be defaulting to the first function...
I know window.location= will only ever trigger GET HTTP verbs - but I'm fine with that for now - surely the method = { RequestMethod.GET, RequestMethod.POST } should cover that?
Does param not work in requestMapping when it's a GET request?
Identified the issue as I was about to hit submit.
My controller turned out to be very old: it was extending AbstractController - which is an implementation of the template method design pattern.
So Spring was looking for specific function names it expected to be implemented, and not using my annotations.
So since at some point in the code's history, someone came along and added annotations, without removing the old inherited request mapping approach - not realizing because there was only one controller function that the annotations did nothing.
Removing the extends AbstractController reference, and ensuring my controller was configured for annotations was the solution.

Spring MVC REST - Block access to Web Methods

I have this method in my Controller that returns a json string to the view
#RequestMapping(value = "/getDevs", method=RequestMethod.GET)
public#ResponseBody String getDevs() throws JsonProcessingException,RemoteException,ServiceException{
ObjectMapper om = new ObjectMapper();
return om.writeValueAsString(WSCall.getDevelopers());
}
I call the method URL using ajax. Everything works fine except I can obtain the json if I put the URL directly in the browser. Is there a way to block this?
I agree with the comments above, that it is not relevant from a security standpoint, but you could probably make use of the X-Requested-With: XMLHttpRequest header that is most likely set for your AJAX requests. I can at least confirm it for jQuery, which might be on your tool stack.
So you could add the headers parameter to your #RequestMapping annotation:
#RequestMapping(value = "/getDevs", method=RequestMethod.GET, headers = { "X-Requested-With=XMLHttpRequest" })
public#ResponseBody String getDevs() throws JsonProcessingException,RemoteException,ServiceException{
[...]
}

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

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