Ambiguous mapping error - spring

I am working on a Spring MVC project in IntelliJ, hosted on Tomcat 6.0.44, using JDK 1.8.0_60. My whole team is running our application without any issues. On my machine, when I try to run the latest code from Git, the same code the rest of the team is running, I get this error:
Context initialization failed Ambiguous mapping. Cannot map 'CartValidate' method.
There are 2 CartValidate methods (one POST, one GET), and a view method in the same controller that seem to be causing this issue.
If I change the request mappings for these 3 methods (I don't have to change the method names, just the request mappings), then it works fine.
I've searched the full codebase for the request mapping values, and they don't occur anywhere else, so I can't understand why there would be a problem with the original request mappings. The original request mappings are cart/validate.json and cart/view.json.
Does anyone have any idea what might be going on?
Here is the code for the 3 methods for which I need to change the request mapping to make the code run:
#ResponseBody
#RequestMapping(value = "/cart/view.json", method = RequestMethod.GET, produces = "application/json", consumes = "application/json")
public Order view(#RequestParam(value = "orderId") Long orderId,
HttpServletRequest aRequest, HttpServletResponse aResponse) {
Order order = orderService.orderGet(aRequest, aResponse, orderId);
return order;
}
#ResponseBody
#RequestMapping(value = "/cart/validate.json", method = RequestMethod.POST, consumes = "application/json", produces = "application/json")
public OrderWeb CartValidate(#RequestBody OrderWeb aModel,
HttpServletRequest aRequest, HttpServletResponse aResponse) {
String orderId = aModel.getOrderId().toString();
OrderWeb order = orderService.orderValidatePost(aModel, orderId,
aRequest, aResponse);
return order;
}
#ResponseBody
#RequestMapping(value = "/cart/validate.json", method = RequestMethod.GET, consumes = "application/json", produces = "application/json")
public OrderWeb CartValidate(#RequestParam(value = "orderId", required = true) Long orderId,
HttpServletRequest aRequest, HttpServletResponse aResponse) {
OrderWeb aModel = (OrderWeb) orderService.orderGet(aRequest, aResponse, orderId);
OrderWeb order = orderService.orderValidatePost(aModel, orderId.toString(),
aRequest, aResponse);
return order;
}

Related

Order of different Spring controller methods by params

There are two methods in the following controller with different params values.
#RestController
#RequestMapping("/api/v1")
public class ToyApiController {
#GetMapping(value = "/toys", params = {"toySize=large"}, produces = MediaType.APPLICATION_JSON_VALUE)
public ToysVO listLargeToys(ListToyCommand listToyCommand) {
// ...
}
#GetMapping(value = "/toys", params = {"country"}, produces = MediaType.APPLICATION_JSON_VALUE)
public ToysVO listToysOfCountry(ListToyCommand listToyCommand) {
// ...
}
}
The API below is expected to hit the listLargeToys method. The behavior is correct.
GET /api/v1/toys?toySize=large&max=10
The API below is expected to hit the listToysOfCountry method. However, the listLargeToys method is hit.
GET /api/v1/toys?country=france&toySize=large&max=10
The code in this post is simplified from actual code. I know that the two methods can be combined into a single method that checks the toySize and country parameters and returns different values accordingly. I'm just wondering whether the listToysOfCountry method can be hit by some tiny changes, e.g. specifying parameters like params without combining the two methods.
GET /api/v1/toys?country=france&toySize=large&max=10
I guess you need some like this:
#RequestMapping(
value = "/toys",
method = RequestMethod.GET,
produces = MediaType.APPLICATION_JSON_VALUE)
#ResponseBody ResponseEntity<String> toys(
#RequestParam(value = "toySize", required = false) String toySize,
#RequestParam(value = "country", required = false) String country,
#RequestParam(value = "max", required = false) Integer max) {
if(null != max && max == 10) {
System.out.println("You send 10 dude.");
}
return new ResponseEntity<String>("{ \"toySize\": "+ toySize +", \"country\": "+ country +", \"max\": "+ max +"}", HttpStatus.OK);
}
Using "required = false", you make param optional :)
By adding a !country in params for listLargeToys API, GET /api/v1/toys?country=france&toySize=large&max=10 no longer goes into listLargeToys API. Instead, it goes into listToysOfCountry as expected.
#RestController
#RequestMapping("/api/v1")
public class ToyApiController {
#GetMapping(value = "/toys", params = {"toySize=large", "!country"}, produces = MediaType.APPLICATION_JSON_VALUE)
public ToysVO listLargeToys(ListToyCommand listToyCommand) {
// ...
}
#GetMapping(value = "/toys", params = {"country"}, produces = MediaType.APPLICATION_JSON_VALUE)
public ToysVO listToysOfCountry(ListToyCommand listToyCommand) {
// ...
}
}

Spring's MockMvc picks up wrong handler method

I have a controller that contains 2+ methods:
#RequestMapping(method = RequestMethod.GET, value = "/{uuid}", produces = "application/json")
public MyObject findByUuid(
#PathVariable("uuid") String uuid) {
...
}
#RequestMapping(method = RequestMethod.POST, value = "/findByStringPropertyEquals", produces = "application/json")
public List<MyObject> findByStringPropertyEquals(
#RequestParam("type") String type,
#RequestParam("property") String property,
#RequestParam("value") String value) {
...
}
Now when I try to test the second method like
mvc.perform(get("/findByStringPropertyEquals?type={0}&property={1}&value={2}", "Person", "testPropInt", 42))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)));
then MockMvc picks up the findByUuid controller method unfortunately. Output is
MockHttpServletRequest:
HTTP Method = GET
Request URI = /api/v1/domain-entities/findByStringPropertyEquals
Parameters = {type=[Person], property=[testPropInt], value=[42]}
Headers = {}
Body = <no character encoding set>
Session Attrs = {}
Handler:
Type = com.example.MyObjectController
Method = public com.example.MyObject com.example.MyObjectController.findByUuid(java.lang.String)
However, accessing the REST API regularly when the web server is started is working fine. Is that a bug or do I do something wrong?
In your code you are invoking a GET:
mvc.perform(get("/findByStringPropertyEquals?type={0}&property={1}&value={2}", "Person", "testPropInt", 42))
.andExpect(status().isOk())
.andExpect(jsonPath("$", hasSize(1)));
However the findByStringPropertyEquals method is declared for POST so your code cannot address that method with a GET. I'm not sure why the MockMVC layer has chosen findByUuid (perhaps because that is the only GET method on this controller) but the reason your code is not hitting findByStringPropertyEquals is that you have chosen the wrong HTTP method.
Try mvc.perform(post(...)) instead.

Getting 400 Bad Request during POST string in spring mvc

i have a rest api that accept a String in POST and return an object,
this is the method:
#RequestMapping(method = RequestMethod.POST, value = "/aValue", headers = "Accept=application/json")
public #ResponseBody
MyObject getMyObject(#RequestBody String string) {
MyObject response = myService.getMyObject(string);
return response;
}
now when i call the api from another service for example, if I do POST like this it gave me always 400 Bad Request:
List<Object> providers = new ArrayList<Object>();
providers.add(jsonProvider);
WebClient client = WebClient.create(baseUrl + myAPI, providers);
client.type(MediaType.APPLICATION_JSON);
client.accept(MediaType.APPLICATION_JSON);
MyObject response = client.post(userId, MyObject.class);
return response;
instead of the working solution i used which is this one:
MyObject response = client.post("\"" + userId + "\"", MyObject.class);
someone could help me ? thanks guys
You're having an issue 'cause what you're posting is not a valid JSON, yet you indicate that it is in your client-side code. As you seem to pass just a simple string property userId you can simply change your mapping to receive plain text by adding consumes = "text/plain",
#RequestMapping(method = RequestMethod.POST, value = "/aValue", headers = "Accept=application/json", consumes = "text/plain")
public #ResponseBody
MyObject getMyObject(#RequestBody String string) {
and have your client send plain text, so
client.type(MediaType.TEXT_PLAIN);

#RequestBody Annotation not working on Websphere Application Server

I am trying to use #RequestBody annotation in one of my controllers method:
#Auditable(application = AuditApplication.DEP_TRXN, actionCategory = AuditActionCategory.READ_RESPONSE, actionDetail = "Viewed a tracking group.", event = AuditEventType.ACTION)
#RequestMapping(value = "groupView", method = RequestMethod.POST, produces = "application/json")
public #ResponseBody
String ajaxGroupView(#RequestBody String payload, final HttpServletRequest request) throws TrackingServiceException, JsonGenerationException, JsonMappingException,
IOException
{
String requestBody = java.net.URLDecoder.decode(payload, "UTF-8");
return requestBody;
}
When i run this code on Jetty server my 'payload' gets the post data, but when i run the same code on Websphere Application Server -8, payload is 'null'. I have mvc annotation driven turn on. How do i make this code work on Websphere Application Server?
Thanks!

Build \ maven clean + install bizarreness

In my project i made the following change:
from:
#RequestMapping(value = "/login/", method = RequestMethod.POST, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(value = HttpStatus.OK)
#ResponseBody
public long getLogin(#RequestBody UserLoginData userData, HttpServletRequest request) {
logger.info(String.format(
Constants.LogMessages.NEW_POST_REQUEST_FROM_IP,
request.getRemoteAddr()));
logger.info("/login/");
long result = userService.getUserByUsernameAndPassword("", "");
return result;
}
to:
#RequestMapping(value = "/login/", method = RequestMethod.GET, consumes = MediaType.APPLICATION_JSON_VALUE)
#ResponseStatus(value = HttpStatus.OK)
#ResponseBody
public long getLogin(#RequestBody UserLoginData userData, HttpServletRequest request) {
logger.info(String.format(
Constants.LogMessages.NEW_POST_REQUEST_FROM_IP,
request.getRemoteAddr()));
logger.info("/login/");
long result = userService.getUserByUsernameAndPassword("", "");
return result;
}
(I change the mapping-requestMethod from POST to GET)
in one of my #controller
thing is - after i made the change, i restarted the tomcat.
and tried to call the controller GET method.
got an error: GET is not supported, but POST is supported.
meaning - the change didn't took place in my project build.
the bizarre thing is after i did project\maven\ run as maven-clean
and right afterwards - project\maven\ run as maven-install
the changes took place!
can anyone explain please why maven has anything to do with my project logic? i thought its for dependencies only...
p.s i got the "build automatically" options set to "true" and i use spring tool suite.
cheers

Resources