Hello there!
The problem is with the Spring-boot-web.
Spring boot version '2.2.1.RELEASE'
The bug :
If you got interface, an abstract class and implement class, the @RequestBody
annotation will not be taken to the implemented class.
Example:
public interface TheControllerInterface {
@PostMapping("/test")
public String returnString(@RequestBody SomeKindOfObject object);
}
public abstract class AbstractController implements TheControllerInterface {
// It might be empty right now, but in overall I had the implementation of few methods here EXCLUDING the one in interface.
}
@RestController
@RequestMapping("/springTest")
public class Controller extends AbstractController {
@Override
public String returnString(SomeKindOfObject object) {
//implementation of method.
}
}
After checking the url endpoint /springTest/test you will see that it does not have body.
Those informations will go to @RequestParam
annotation instead of @RequestBody
annotation.
The url will be inheritated. Just the @RequestBody
annotation will not.
Best!
PS. It looks like somebody has RequestBody username in github, to using "@" with Annotation name will provide link to that username :(
Comment From: rstoyanchev
I've edited your comment to improve the formatting. You might want to check out this Mastering Markdown guide for future reference. In particular for mentions, those can be escaped with backticks.
Comment From: rstoyanchev
I cannot reproduce this. Support for method parameter annotations on an interface added in 5.1 with #15682. Please provide a sample that demonstrates the issue.
Comment From: cogunites
@rstoyanchev
My repo is created exactly with that problem. You have there Swagger configured + the specific situation.
If you will be in my repo (or you will download it) you will see two controllers. MobileUserController and WebUserController. Both are extending the AbstractController, while AbstractController is implementing the Controller interface.
Controller interface got one method.
@PostMapping
User saveUser(@RequestBody User user);
As you can see, there is @RequestBody
annotation inside the method param.
Now, going back to implemented controller.
You have that method overriden in both, as AbstractController does not implement that method.
In swagger you will be able to see, that WebUserController
```@RestController @AllArgsConstructor @RequestMapping("/web") public class WebUserController extends AbstractController {
@Override
public User saveUser(@RequestBody User user) {
return null;
}
}
Has body able to send,
while the second controller
```@RestController
@AllArgsConstructor
@RequestMapping("/mobile")
public class MobileUserController extends AbstractController {
@Override
public User saveUser(User user) {
return null;
}
}
Would be sended through Query Params ( @QueryParam
) for each field that user got.
I would expect to @RequestBody
will be propagated throught every class up to implemented method. In this case up to MobileUserController and WebUserController.
Hope this helps.
PS. Ignore the return null;
lines. Those are not important here.
Comment From: rstoyanchev
Thanks for the sample. This works as expected:
[master]$ curl -v -H "Content-Type:application/json" -d '{"email":"s","name":"s","password":"s"}' http://localhost:8080/mobile
* Trying 127.0.0.1...
* TCP_NODELAY set
* Connected to localhost (127.0.0.1) port 8080 (#0)
> POST /mobile HTTP/1.1
> Host: localhost:8080
> User-Agent: curl/7.58.0
> Accept: */*
> Content-Type:application/json
> Content-Length: 39
>
* upload completely sent off: 39 out of 39 bytes
< HTTP/1.1 200
< Content-Length: 0
< Date: Thu, 05 Dec 2019 12:55:36 GMT
<
* Connection #0 to host localhost left intact
The problem you're pointing out is with how Springfox interprets those annotations. I believe this here is the same underlying issue https://github.com/springfox/springfox/issues/2392.
Comment From: cogunites
@rstoyanchev to be honest, the problem is that if you sent it like you did, will not apply to body itself. Values will be null.
But only inside the second controller will be fine.
If that works as excepted, than fine.
So if you will add @RequestBody
it will work properly (Try the same with web controller).
So in fact, you need to use query params in the postman / curl.
Ofc, that will work while sending the request. It will accept the body, as this is Post HTTP method, but will not get the body from the request inside the spring.
Comment From: rstoyanchev
My mistake, I was partly thrown off by the swagger screens. Indeed it does look like when @RequestBody
is on an interface declared on a base class, it is not seen at runtime, and the argument is treated as @ModelAttribute
by default.
Comment From: OLPMO
I made some debug and agreed with the rstoyanchev's conjecture.
Comment From: jhoeller
It turns out that our support for parameter annotations in an interface (#15682) only operated on the interfaces implemented by the declaring class directly. As of 5.2.5, we take interfaces across the entire class hierarchy into account. I'll use this issue to track that generalized effort, also considering a backport to 5.1.15.