Two similar Spring endpoints, wrong one is being called - spring-boot

I have two endpoints in the same controller file
#PostMapping("{app}/{schema}")
public ResponseEntity createResource(
#PathVariable final String app,
#PathVariable final String schema,
#RequestBody final Object input,
final HttpServletRequest request) {
//do stuff
}
#PostMapping("{app}/bulk")
public ResponseEntity bulk(
#PathVariable final String app,
#RequestBody final Object input,
final HttpServletRequest request) {
//do stuff
}
The createResource gets called when I do POST app/bulk.

Your first endpoint
#PostMapping("{app}/{schema}")
is a wildcard endpoint due to two PathVariables. Therefor, any request to a path yourapp.com/xyz is caught by it and the second endpoint is always ignored.
To solve this, you should either create two distinct endpoints without using the {schema} PathVariable like you are doing in the second endpoint, or just use the first one as is and check the {schema} variable for the current path in //do stuff.

This is because when the URL gets resolve, your first endpoint will perfectly fit in.
#PostMapping("{app}/{schema}")
Here bulk will be resolve as {schema} string. You can avoid it by defining a unique name like this :
#PostMapping("{app}/schema/{schema}")
This should solve your problem.

You should merge two endpoint like this
#PostMapping("{app}/{schema}")
public ResponseEntity createResource(
#PathVariable final String app,
#PathVariable final String schema,
#RequestBody final Object input,
final HttpServletRequest request) {
if ("bulk".equals(schema)) {
// do bulk stuff
} else {
//do stuff
}
}

Related

Error 400 when receiving data from URL parameters en Spring MVC

I am trying to receive data from an URL with two parameters like this one:
http://localhost:80000/xxx/xxx/tickets/search?codprovincia=28&municipio=110000
No matter the approach, I am always getting a 400 error, but if I access the URL without the two parameters, the controller returns the view correctly (without the parameters, naturally)
This is the code of my controller:
#Controller
#RequestMapping(value = "/xxx" )
public class BuscadorIncidenciasController extends BaseControllerWeb {
#RequestMapping("tickets")
public String tickets(Model model, #RequestParam ("codprovincia") String codprovincia, #RequestParam ("municipio") String municipio, HttpServletRequest request) throws NoAjaxException {
//...
return CONST.JSP_VIEW;
}
...}
Extra info: if I use this URL:
http://localhost:9081/xxx/xxx/tickets/search/28/790000
And this code:
#Controller
#RequestMapping(value = "/xxx" )
public class BuscadorIncidenciasController extends BaseControllerWeb {
#RequestMapping(value = "buscar/{codprovincia}/{municipio}", method = RequestMethod.GET)
public String buscar(#PathVariable Integer codprovincia, #PathVariable Integer municipio ,Model model, HttpServletRequest request) throws NoAjaxException {
//...
return CONST.JSP_VIEW;
}
...}
It gets the parameters correctly. The problem is that I have to use the first URL. I have reviewed similar questions about similar issues, and I have implemented the solutions to those issues, but I get the 400 error regardless what I try (add value="xxx=, required=false, and other suggestions.)
For RequestParam, you need to explicitly add 'name' attribute
#RequestParam(name = "codprovincia"), #RequestParam (name = "municipio")
No need to for HttpServletRequest, unless you have reason
Also, in your 'tickets' method, RequestMapping is not conforming to your URL path.
I think it should be
#RequestMapping("/xxx/tickets/search")
Cheers!

Spring - Query parameters without question mark

I'm having an issue parsing an URL with Spring.
My endpoint is
#RequestMapping(path = "/register", method = RequestMethod.GET)
public String userActivation(#RequestParam("token") String token, #RequestParam("code") String code, final Map<String, Object> model) {
...
}
So I am expecting a token and a code in the URL.
The problem I am facing is that the service redirecting to my page omits the question mark, something like:
http://myapp/register/&token=sdgddfs&code=fdasgas
Which Spring fails to match to my endpoint.
Is there any way to handle this?
You can re-write you method using #PathVariable instead of #RequestParam
So you'll have an URL like http://myapp/register/sdgddfs/fdasgas, and an annotation for method
#RequestMapping(path = "/register/{token}/{code}")
public String userActivation(#PathVariable("token") String token, #PathVariable("code") String code) { ... }

#RequestHeader mapped value contains header twice

We are facing a super strange problem: in our endpoint:
#PostMapping(value = "/send_event_to_payment_process")
#Async
public void sendEvent(#Valid #RequestBody final SendEventRequestDto dto, #RequestHeader(value = TENANT) String foo) {
the mapped #RequestHeader variable foo contains the vaue twice joined with a ',' ("test,test"). If we read the header programmatically using the request context:
public void sendEvent(#Valid #RequestBody final SendEventRequestDto dto, #Context final HttpServletRequest request) {
final String tenant = request.getHeader(TENANT);
we receive the proper value (only once: "test").
Any clues what the problem might be?!
Thank you!
You are comparing different things.
The HttpServletRequest.getHeader method always returns a single value, even if there are multiple values for the header. It will return the first (see the javadoc of the method).
Spring uses the HttpServletRequest::getHeaders method to get all the values. Which retrieves all header values and, depending on the value, return the String[] or creates a single concatenated String.
To compare the same things you also should use the getHeaders method and then you will have the same result. Which means your request contains 2 header values for the given header.

how to capture multiple parameters using #RequestParam using spring mvc?

Suppose a hyperlink is clicked and an url is fired with the following parameter list myparam=myValue1&myparam=myValue2&myparam=myValue3 . Now how can I capture all the parameters using #RequestParam in spring mvc?
My requirement is I have to capture all the params and put them in a map.
Please help!
#RequestMapping(value = "users/newuser", method = RequestMethod.POST)
public String saveUser(#RequestParam Map<String,String> requestParams) throws Exception{
String userName=requestParams.get("email");
String password=requestParams.get("password");
//perform DB operations
return "profile";
}
You could use RequestParam in the above mentioned manner.
It seems you can't get
Map<String,String>
because all your params have same name "myparam"
Try this instead:
public ModelAndView method(#RequestParam("myparam") List<String> params) { }
To get all parameters at once try this:
public ModelAndView postResultPage(#RequestParam MultiValueMap<String, String> params)
This feature is described in the #RequestParam java doc (3. Paragraph):
Annotation which indicates that a method parameter should be bound to a web request parameter. Supported for annotated handler methods in Servlet and Portlet environments.
If the method parameter type is Map and a request parameter name is specified, then the request parameter value is converted to a Map assuming an appropriate conversion strategy is available.
If the method parameter is Map<String, String> or MultiValueMap<String, String> and a parameter name is not specified, then the map parameter is populated with all request parameter names and values.
As of Spring 3.0, you can also use MultiValueMap to achieve this:
A rudimentary example would be:
public String someMethod(#RequestParam MultiValueMap<String,String> params) {
final Iterator<Entry<String, List<String>>> it = params.entrySet().iterator();
while(it.hasNext()) {
final String k = it.next().getKey();
final List<String> values = it.next().getValue();
}
return "dummy_response";
}
If anyone is trying to do the same in Spring Boot, use RequestBody in place of RequestParam
Spring mvc can support List<Object>, Set<Object> and Map<Object> param, but without #RequestParam.
Take List<Object> as example, if your object is User.java, and it like this:
public class User {
private String name;
private int age;
// getter and setter
}
And you want pass a param of List<User>, you can use url like this
http://127.0.0.1:8080/list?users[0].name=Alice&users[0].age=26&users[1].name=Bob&users[1].age=16
Remember to encode the url, the url after encoded is like this:
http://127.0.0.1:8080/list?users%5B0%5D.name=Alice&users%5B0%5D.age=26&users%5B1%5D.name=Bob&users%5B1%5D.age=16
Example of List<Object>, Set<Object> and Map<Object> is displayed in my github.
You can use for multiple Params as such
public String saveUser(#RequestParam("email") String userName, #RequestParam("password") String password) throws Exception{
//your code
//perform DB operations
return "profile";
}
For params with same name, you can use MultiValueMap<String ,String>. Then all the values would be present as List
You can use multiple #RequestParam annotations as shown below.
#RequestParam(value="myparam1", required = true) <Datatype> myparam1,
#RequestParam(value = "myparam2", required = false) <Datatype> myparam2,

Spring request mapping with Paypal

For the return URL, it seem you have to define the whole URL like so:
String returnURL = "http://localhost:8080/appName/shopping/confirmorder";
Now, I have a problem with the request mapping:
#RequestMapping(value = "/shopping/confirmorder?token={token}&PayerID={payerID}", method = RequestMethod.GET)
public String doGet(#PathVariable("token") String token, #PathVariable("payerID") String payerID,
HttpServletRequest request) {
// do stuff
}
The controller is never called for some reason?
The final returnURL returned from Paypal is like this:
http://localhost:8080/appName/shopping/confirmorder?token=EC-4...G&PayerID=A...W
Note the Ids have been edited.
If you have two path variables named token and payerID, then the method signature should be
public void doGet(#PathVariable("token") String token,
#PathVariable("payerID") String token,
HttpServletRequest request,
HttpServletResponse response)
How did you expect Spring to put those two strings inside a single option parameter of type int?
See http://static.springsource.org/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-requestmapping-uri-templates
Moreover, PathVariable is used to bind portions of the request path to method arguments. In your case, you have request parameters. You should thus use #RequestParam:
#RequestMapping(value = "/shopping/confirmorder", method = RequestMethod.GET)
public void doGet(#RequestParam("token") String token,
#RequestParam("PayerID") String token,
HttpServletRequest request,
HttpServletResponse response)
See http://static.springsource.org/spring/docs/current/spring-framework-reference/html/mvc.html#mvc-ann-requestparam

Resources