spring-cloud / spring-cloud-openfeign

Support for using OpenFeign in Spring Cloud apps
Apache License 2.0
1.22k stars 786 forks source link

OpenFeign doesn't work with kotlin suspend method #661

Open XhstormR opened 2 years ago

XhstormR commented 2 years ago

I have an API interface where the methods are kotlin suspend methods:

@FeignClient(path = "/api/masker", contextId = "MaskerApi", name = Const.SERVICE, configuration = [FeignConfig::class])
interface MaskerApi {

    @PostMapping("/mask")
    suspend fun mask(
        @Valid @RequestBody maskRequest: MaskRequest,
    ): RestResponse<List<CharSequence>>

    @PostMapping("/matches")
    suspend fun matches(
        @Valid @RequestBody maskRequest: MaskRequest,
    ): RestResponse<List<Boolean>>
}

But when I run the program, the program throws an exception:

Caused by: java.lang.IllegalStateException: Method has too many Body parameters: public abstract java.lang.Object io.github.xhstormr.masker.web.api.masker.MaskerApi.matches(io.github.xhstormr.masker.model.masker.MaskRequest,kotlin.coroutines.Continuation)
Warnings:
- 
    at feign.Util.checkState(Util.java:121) ~[feign-core-11.7.jar:na]
    at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:142) ~[feign-core-11.7.jar:na]
    at org.springframework.cloud.openfeign.support.SpringMvcContract.parseAndValidateMetadata(SpringMvcContract.java:193) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0]
    at feign.Contract$BaseContract.parseAndValidateMetadata(Contract.java:65) ~[feign-core-11.7.jar:na]
    at feign.ReflectiveFeign$ParseHandlersByName.apply(ReflectiveFeign.java:151) ~[feign-core-11.7.jar:na]
    at feign.ReflectiveFeign.newInstance(ReflectiveFeign.java:49) ~[feign-core-11.7.jar:na]
    at feign.Feign$Builder.target(Feign.java:268) ~[feign-core-11.7.jar:na]
    at org.springframework.cloud.openfeign.DefaultTargeter.target(DefaultTargeter.java:30) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0]
    at org.springframework.cloud.openfeign.FeignClientFactoryBean.loadBalance(FeignClientFactoryBean.java:373) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0]
    at org.springframework.cloud.openfeign.FeignClientFactoryBean.getTarget(FeignClientFactoryBean.java:421) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0]
    at org.springframework.cloud.openfeign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:396) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0]
    at org.springframework.cloud.openfeign.FeignClientsRegistrar.lambda$registerFeignClient$0(FeignClientsRegistrar.java:235) ~[spring-cloud-openfeign-core-3.1.0.jar:3.1.0]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.obtainFromSupplier(AbstractAutowireCapableBeanFactory.java:1249) ~[spring-beans-5.3.14.jar:5.3.14]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1191) ~[spring-beans-5.3.14.jar:5.3.14]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:582) ~[spring-beans-5.3.14.jar:5.3.14]
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:542) ~[spring-beans-5.3.14.jar:5.3.14]
    ... 28 common frames omitted

This kotlin.coroutines.Continuation parameter is generated by the kotlin compiler at compile time, so I can't modify this parameter, how can I tell FeignClient to ignore this Continuation parameter?

XhstormR commented 2 years ago

When I make a successful call to make an http request, but it fails when converting the http call result json into an object. When I remove the suspend modifier in the method, try again and it will be successful.

java.lang.IllegalStateException: Failed to execute ApplicationRunner
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:761) ~[spring-boot-2.6.2.jar:2.6.2]
    at org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:748) ~[spring-boot-2.6.2.jar:2.6.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:309) ~[spring-boot-2.6.2.jar:2.6.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1301) ~[spring-boot-2.6.2.jar:2.6.2]
    at org.springframework.boot.SpringApplication.run(SpringApplication.java:1290) ~[spring-boot-2.6.2.jar:2.6.2]
    at io.github.xhstormr.masker.ApplicationKt.main(Application.kt:54) ~[main/:na]
Caused by: java.lang.ClassCastException: class java.util.LinkedHashMap cannot be cast to class io.github.xhstormr.masker.model.response.RestResponse (java.util.LinkedHashMap is in module java.base of loader 'bootstrap'; io.github.xhstormr.masker.model.response.RestResponse is in unnamed module of loader 'app')
    at io.github.xhstormr.masker.web.api.masker.MaskerApi.maskSync(MaskerApi.kt:32) ~[main/:na]
    at java.base/java.lang.invoke.MethodHandle.invokeWithArguments(MethodHandle.java:710) ~[na:na]
    at feign.DefaultMethodHandler.invoke(DefaultMethodHandler.java:141) ~[feign-core-11.7.jar:na]
    at feign.ReflectiveFeign$FeignInvocationHandler.invoke(ReflectiveFeign.java:100) ~[feign-core-11.7.jar:na]
    at com.sun.proxy.$Proxy116.maskSync(Unknown Source) ~[na:na]
    at io.github.xhstormr.masker.Application.init$lambda-0(Application.kt:45) ~[main/:na]
    at org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:758) ~[spring-boot-2.6.2.jar:2.6.2]
    ... 5 common frames omitted
OlgaMaciaszek commented 2 years ago

Hello, @XhstormR. We have never introduced kotlin support in Spring Cloud OpenFeign, so there might be many Kotlin constructs that will not work. Putting the issue into ice-box as currently we are not working on Kotlin support.

aSemy commented 2 years ago

It looks like OpenFeign recently merged in support for Kotlin suspend functions https://github.com/OpenFeign/feign/pull/1706. When that PR is released and spring-cloud-openfeign can update, will suspend functions 'just work'?

OlgaMaciaszek commented 2 years ago

We'll review it. If it requires only minimal changes, we'll possibly implement support here also. However, if it's more involved, then Kotlin support is currently not a priority. However, we would review a PR if it's submitted.

XhstormR commented 1 year ago

OpenFeign 12.0 was released with support for Kotlin coroutines. By looking at the test case CoroutineFeignTest, we simply need to build the Client using CoroutineFeign to get invocation support for the Kotlin coroutine.

https://github.com/OpenFeign/feign/blob/master/kotlin/src/test/kotlin/feign/kotlin/CoroutineFeignTest.kt#L148

Alex-Schiff commented 9 months ago

Since the Feign class and the CoroutineFeign class have nothing in common, it is not trivial to add this support to spring-cloud-openfeign. I think it is a better fit for https://github.com/spring-projects-experimental/spring-cloud-openfeign-async if that ever gets written.