Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing interface in abstract class does not expose parameter annotations #24127

Closed
cogunites opened this issue Dec 3, 2019 · 8 comments
Assignees
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Milestone

Comments

@cogunites
Copy link

cogunites commented Dec 3, 2019

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 :(

@spring-projects-issues spring-projects-issues added the status: waiting-for-triage An issue we've not yet triaged or decided on label Dec 3, 2019
@rstoyanchev
Copy link
Contributor

rstoyanchev commented Dec 4, 2019

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.

@rstoyanchev
Copy link
Contributor

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.

@rstoyanchev rstoyanchev added status: waiting-for-feedback We need additional information before we can continue status: waiting-for-triage An issue we've not yet triaged or decided on and removed status: waiting-for-triage An issue we've not yet triaged or decided on labels Dec 4, 2019
@cogunites
Copy link
Author

cogunites commented Dec 4, 2019

@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

@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

@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.
MobileUserController
WebUserController

Hope this helps.

PS. Ignore the return null; lines. Those are not important here.

@spring-projects-issues spring-projects-issues added status: feedback-provided Feedback has been provided and removed status: waiting-for-feedback We need additional information before we can continue labels Dec 4, 2019
@rstoyanchev
Copy link
Contributor

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 springfox/springfox#2392.

@rstoyanchev rstoyanchev added for: external-project Needs a fix in external project and removed status: feedback-provided Feedback has been provided status: waiting-for-triage An issue we've not yet triaged or decided on labels Dec 5, 2019
@cogunites
Copy link
Author

cogunites commented Dec 5, 2019

@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.

effect in code
Postman request

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.

@rstoyanchev
Copy link
Contributor

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.

@rstoyanchev rstoyanchev reopened this Dec 5, 2019
@rstoyanchev rstoyanchev added in: web Issues in web modules (web, webmvc, webflux, websocket) type: bug A general bug and removed for: external-project Needs a fix in external project labels Dec 5, 2019
@rstoyanchev rstoyanchev added this to the 5.2.3 milestone Dec 5, 2019
@OLPMO
Copy link
Contributor

OLPMO commented Dec 10, 2019

I made some debug and agreed with the rstoyanchev's conjecture.

@rstoyanchev rstoyanchev modified the milestones: 5.2.3, 5.2.4 Jan 13, 2020
@jhoeller jhoeller modified the milestones: 5.2.4, 5.2.5 Feb 20, 2020
@jhoeller jhoeller self-assigned this Mar 19, 2020
@jhoeller jhoeller changed the title Implementing interface in abstract classes does not propagate @RequestBody Implementing interface in abstract class does not expose parameter annotations Mar 21, 2020
@jhoeller
Copy link
Contributor

jhoeller commented Mar 21, 2020

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
in: web Issues in web modules (web, webmvc, webflux, websocket) status: backported An issue that has been backported to maintenance branches type: bug A general bug
Projects
None yet
Development

No branches or pull requests

5 participants