Closed spring-projects-issues closed 5 years ago
George Georgovassilis commented
There is an implementation here: https://github.com/ggeorgovassilis/spring-rest-invoker I would be happy to contribute it as a module or to spring-web if there is interest
George Georgovassilis commented
Upon re-reading the ticket's description, the submitter asks for a common interface that would work both for controllers that expose REST APIs and clients that consume them. The implementation above generally requires interfaces that might not make sense when implemented by a controller.
Rossen Stoyanchev commented
Controller annotations can be inherited from a base class or interface. Method parameter annotations however cannot be. They must be directly on the controller method in all cases. This ticket is a request to change that.
Indeed your proposal has similar purpose but does not depend on this ticket since in your case the interfaces are only used for generating a client proxy.
That said once the ability to create a client proxy from an interface with the same annotations is available, it becomes a natural question: should it be possible for a controller and a client proxy to share an interface?
George Georgovassilis commented
In that case I'd like to decouple my implementation from the concrete decision whether a single or multiple interfaces are to be used. New ticket #16747.
Matt Benson commented
This issue also applies to parent classes.
Matt Benson commented
PR submitted: https://github.com/spring-projects/spring-framework/pull/976
Rossen Stoyanchev commented
Wow this is a very extensive effort and a perfect example of the sort of thing that you could discuss (beforehand) on our contributor's mailing list.
Style and programming model issues aside, there is a whole separate discussion if we should even re-use the same annotations for client-proxy generation or create more dedicated annotations for the purpose (see #16827). Alternatively with what we have today wouldn't it be easy to generate interfaces on the fly from a controller's methods and annotations? That way method parameter annotations can remain close to the class as promoted by the programming model today.
Matt Benson commented
TBH I decided to take my chances rather than subject my inbox to the burden of another mailing list. With regard to client proxies, spring-cloud is already doing this with their support for Netflix Feign, and until recently their documentation (at least somewhat erroneously) reported that it was possible to use a Spring-annotated REST interface for client and server alike. The non-inherited nature of parameter annotations is in fact the missing ingredient here. If people are going to use interfaces for client proxies, I can't see how forcing them to define their REST API in multiple places does anything other than expose them to the risk that these definitions will fall out of sync.
My interpretation of "on the fly" is "at runtime." I don't see how that helps clients who want to consume the hypothetical generated interface. Certainly it would be possible to generate such an interface, but it seems like this would be more a tooling step or, at best, an annotation processor, and getting these generated types into a consumable client artifact would be quite a build-system-dependent task. It is not at all clear to me that this solution would be better than a basic, Java-based one.
Matt Benson commented
With the recent closure of the development forum, do any other Spring developers have any thoughts on the proposed fix?
Rossen Stoyanchev commented
If we did implement this feature to allow method parameter annotations on supertypes it would most likely be exposed through AnnotationUtils
which we currently use to find method and type level annotations on supertypes (see for example RequestMappingHandlerMapping
). The various argument resolver implementations would delegate to AnnotationUtils
unlike the PR which introduces the same as base class functionality. Also the current algorithm in AnnotationUtils
for method and type level annotations is also a lot more involved and so would be the one for method parameters. This is not really meant as criticism but just saying that the problem is more complex. We are however taking a look at this still. There are several related tickets so I'm going to assign this one also to Juergen Hoeller for consideration.
From a Spring MVC perspective I think basic support for the Spring Cloud with Netflix Feign use case (i.e. allow the same interface to be used on client and server side)is fine as a use case but allowing a method parameter to be anywhere is another matter. It means for a controller method with several arguments the annotations for each argument could literally be in a different superclass. I'm not sure it should be so wide open. Some meaningful constraints focusing on the target use cases we want to support should perferably be in order.
Alexandre Navarro commented
+1
Elias O commented
One more use case for method param annotation inheritance: I'm using Swagger Codegen to generate API interfaces from OpenAPI specs during build process so that I can implement them directly in my code. Those generated interfaces contain all Spring MVC related annotations so I don't need to care about annotating my methods. Except method params. I had to copy them once again because Spring doesn't see them.
Yura Nosenko commented
@Elias
O, have a similar story with RAML. It's a huge gap, since the method-level annotations work as expected. This feature is a MUST!
Yura Nosenko commented
When it comes to validation, there is a workaround - @Validated
. It will actually make jsr303 param annotations inheritable.
Rossen Stoyanchev commented
Do you mean that Swagger can't generate a class for a server stub, and same for RAML?
Elias O commented
Rossen Stoyanchev that mean that if you use Swagger codegen, you can choose an "API [interfaces] only" option that generates API interfaces like this:
public interface Resource {
@RequestMapping
void do(@RequestParam String name, @RequestBody DTO value);
}
so that you may expect your controller to look like this:
@RestController
public class ResourceImpl implements Resource {
void do(String name, DTO value) {
// do stuff
}
}
ResourceImpl#do will inherit @RequestMapping
, but @RequestParam
and @RequestBody
won't, therefore name
and value
will be null (AFAIR).
The only way to make it work is to duplicate arg annotations:
@RestController
public class ResourceFixedImpl implements Resource {
void do(@RequestParam String name, @RequestBody DTO value) {
// do stuff
}
}
In real cases, such interfaces can have tons of annotations (moreover, arg annotations tend to become very long) and it makes sense to leave them aside from the implementation.
To overcome this bug, currently swagger can generate api + implementation (with annotated params) + delegation interface, so that users can implement a delegation interface only.
Alexandre Navarro commented
Hi,
I will explain our use case why we want to implement @FeignClient
interface in the @RestController
.
In my firm, we have developped java microservice (around 20) with spring, spring-boot and spring-cloud with @FeignClient
since 3-4 years. Our different microservices are in different git repo, can be released independently and at first each microservice implements their own @FeignClient
if they need to request another microservice, so our microservices are really independent each other. One day, someone corrected a typo from "portofolio" to "portfolio" in an argument of an annotation of a @RestController
, he changed in the controller and some feign clients but missed one feign client. We had a bug in production we have to release a fix. Since this moment, we decided to change our strategy with @FeignClient
, we decided to share it, not duplicate it. Now, our microservice are splitted in 2 modules, one for the microservice itself, one for the api which include the POJO used as argument or result of the @RestController
an @FeignClient
Interface. If another microservice2 needs to call microservice1, he will take microservice2-api as lib and it uses it in order not to copy all Feign interfaces everywhere. Clearly, we are happier with shared @FeignClient
with shared-lib-api (we only used the api lib internally to the team not with the others which consume our microservice). Nevertheless, today, as the @RestController
can't implement the @FeignClient
interface because the annotation in the argument are not taken, we need to duplicate the different annotations between @RestController
and @FeignClient
and we added some IntegrationTest to test if our @FeignClient
interface are coherent with the @RestController
, which is clearly painful and can be fixed if a @RestController
can implement @FeignClient
interface.
So this is the reason why I really want to have this feature.
I understand you have to set some rules if for instance some annotations in the implementation are different in the interface (potentially they can be contradictory).
In my point of view, you should take first the annotations of the interface and potentially add / override it if the implementation add / override some annotations. It can be useful because
@RestController
) are always implemented first compared to annotation in client side (@FeignClient
)This is just my point of view on the rule to adopt between interfact / implemenation annotations, maybe you have some other arguments to decide apply different rules between annotations declared in @ClientFeign
vs @RestController
Thanks in advance to implement this feature, it will be really really useful.
@Juergen
Hoeller : Just to remember, we discussed about it at the Spring BOF at Devoxx France 2 weeks ago.
Damian Sima commented
Hi guys, can we have an update from the eng team about this?
I mean the issue was open un 2013 and it is still active but unresolved, are we gonna have a feature for this?
Cheers!
Andrey Gliznetsov commented
I'd like to add that some annotations actually work on interface, f.e. @RequestParam
, but not @RequestBody
nor @PathVariable
Juergen Hoeller commented
I'm happy to report that this is finally resolved in master now, in time for the 5.1 RC1 release!
Feel free to give it an early try against the upcoming 5.1.0.BUILD-SNAPSHOT
...
George Georgovassilis commented
Thank you Juergen Hoeller
Damian Sima commented
you rock Juergen Hoeller
@jhoeller hi,how can I achieve the same effect in spring5.0.x?
Paweł Mendelski opened SPR-11055 and commented
Please, enable inheritance of controller related annotations from implemented interfaces.
I would like to share rest service interface between client and server. This way I could provide a really convenient mechanism - client-proxy, like the one RestEasy provides.
Shared interface:
Server implementation:
Client code:
At the moment I cannot write
SpringMvcProxyFactory
because parameter annotations from interfaceRandomDataController
are not inherited byRandomDataControllerImpl
.Affects: 4.3.3
Issue Links:
20270
@RequestBody
annotation in interface is ignored ("is duplicated by")18593 Inconsistent interpretation of parameter annotations in interface-based handler methods ("is duplicated by")
19095 mvc Parameter annotations are not inherited ("is duplicated by")
19371 Spring Mvc Annotations in argument of a method (like
@Path
) annotated with@RequestMapping
are not supported when you create a@RestController
via an Interface ("is duplicated by")19590 RequestHeader annotation does not work if it's put on an interface ("is duplicated by")
16827 Consider reusing
@RequestMapping
annotations as an interface for HTTP clients ("is duplicated by")19612 Allow controller parameter annotations (
@RequestBody
,@PathVariable
...) to be defined on interfaces or parent classes ("is duplicated by")20658 Supports annotating parameter annotations like
@PathVariable
on interface method ("is duplicated by")21992 Aliases in parameter annotations on a controller interface are not evaluated
22160 MethodParameter.isOptional() fails with ArrayIndexOutOfBoundsException
16747 Introduce proxy-based REST client similar to HttpInvokerProxyFactoryBean
21756 getMappingForMethod failed when implement a interface method(with
@RequestMapping
) in super class20609 Annotations on generic interface methods not found by AnnotationUtils
21416 Subclass-resolved generic return type declarations for handler methods
21380
@DateTimeFormat
annotations are necessary both on field and constructor parameter in the immutable form classReferenced from: commits https://github.com/spring-projects/spring-framework/commit/790d515f8c49d9f80f41a2f0ca00535fa930ccc2, https://github.com/spring-projects/spring-framework/commit/1f5d0faf1f3a1ee1b8e26b76cd72a5fab3c2839b
30 votes, 37 watchers