When an interface has @PathVariable in it, feign fails to generate a client for it.
@FeignClient("HelloServer")
public interface ITestController {
@RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
String hello(@PathVariable String name);
@RequestMapping(value = "/hello", method = RequestMethod.GET)
String hello();
@RequestMapping(value = "/randomJob", method = RequestMethod.GET)
JobDto getRandomJob();
}
Client Code:
@SpringBootApplication
@RestController
@ComponentScan("client")
@EnableFeignClients
@EnableDiscoveryClient
public class ClientApplication {
@Autowired
ITestController client;
@RequestMapping("/hello")
public String hello() {
return client.hello();
}
@RequestMapping("/hello/{name}")
public String helloWithName(@PathVariable String name) {
return client.hello(name);
}
@RequestMapping("/job")
public JobDto randomJob() {
return client.getRandomJob();
}
public static void main(String[] args) {
SpringApplication.run(ClientApplication.class, args);
}
}
Exception:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'clientApplication': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: client.ITestController client.ClientApplication.client; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'client.ITestController': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: PathVariable annotation was empty on param 0.
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:334) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1214) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:543) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:482) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:306) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:230) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:302) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:772) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:839) ~[spring-context-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:538) ~[spring-context-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.boot.context.embedded.EmbeddedWebApplicationContext.refresh(EmbeddedWebApplicationContext.java:118) ~[spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]
at org.springframework.boot.SpringApplication.refresh(SpringApplication.java:766) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]
at org.springframework.boot.SpringApplication.createAndRefreshContext(SpringApplication.java:361) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:307) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1191) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]
at org.springframework.boot.SpringApplication.run(SpringApplication.java:1180) [spring-boot-1.3.2.RELEASE.jar:1.3.2.RELEASE]
at client.ClientApplication.main(ClientApplication.java:42) [main/:na]
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.8.0_45]
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62) ~[na:1.8.0_45]
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.8.0_45]
at java.lang.reflect.Method.invoke(Method.java:497) ~[na:1.8.0_45]
at com.intellij.rt.execution.application.AppMain.main(AppMain.java:144) [idea_rt.jar:na]
Caused by: org.springframework.beans.factory.BeanCreationException: Could not autowire field: client.ITestController client.ClientApplication.client; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'client.ITestController': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: PathVariable annotation was empty on param 0.
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:573) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:88) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:331) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
... 22 common frames omitted
Caused by: org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'client.ITestController': FactoryBean threw exception on object creation; nested exception is java.lang.IllegalStateException: PathVariable annotation was empty on param 0.
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:175) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.getObjectFromFactoryBean(FactoryBeanRegistrySupport.java:103) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getObjectForBeanInstance(AbstractBeanFactory.java:1590) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:254) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:197) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:1192) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:1116) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:1014) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:545) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
... 24 common frames omitted
Caused by: java.lang.IllegalStateException: PathVariable annotation was empty on param 0.
at feign.Util.checkState(Util.java:108) ~[feign-core-8.1.1.jar:8.1.1]
at org.springframework.cloud.netflix.feign.annotation.PathVariableParameterProcessor.processArgument(PathVariableParameterProcessor.java:49) ~[spring-cloud-netflix-core-1.1.0.M5.jar:1.1.0.M5]
at org.springframework.cloud.netflix.feign.support.SpringMvcContract.processAnnotationsOnParameter(SpringMvcContract.java:169) ~[spring-cloud-netflix-core-1.1.0.M5.jar:1.1.0.M5]
at feign.Contract$BaseContract.parseAndValidatateMetadata(Contract.java:75) ~[feign-core-8.1.1.jar:8.1.1]
at feign.Contract$BaseContract.parseAndValidatateMetadata(Contract.java:49) ~[feign-core-8.1.1.jar:8.1.1]
at feign.ReflectiveFeign$ParseHandlersByName.apply(ReflectiveFeign.java:137) ~[feign-core-8.1.1.jar:8.1.1]
at feign.ReflectiveFeign.newInstance(ReflectiveFeign.java:55) ~[feign-core-8.1.1.jar:8.1.1]
at feign.Feign$Builder.target(Feign.java:166) ~[feign-core-8.1.1.jar:8.1.1]
at org.springframework.cloud.netflix.feign.FeignClientFactoryBean$DefaultTargeter.target(FeignClientFactoryBean.java:203) ~[spring-cloud-netflix-core-1.1.0.M5.jar:1.1.0.M5]
at org.springframework.cloud.netflix.feign.FeignClientFactoryBean.loadBalance(FeignClientFactoryBean.java:153) ~[spring-cloud-netflix-core-1.1.0.M5.jar:1.1.0.M5]
at org.springframework.cloud.netflix.feign.FeignClientFactoryBean.getObject(FeignClientFactoryBean.java:173) ~[spring-cloud-netflix-core-1.1.0.M5.jar:1.1.0.M5]
at org.springframework.beans.factory.support.FactoryBeanRegistrySupport.doGetObjectFromFactoryBean(FactoryBeanRegistrySupport.java:168) ~[spring-beans-4.2.5.RELEASE.jar:4.2.5.RELEASE]
... 32 common frames omitted
Comment From: Koizumi85
Hello,
not tested, but i believe you have to supply the path variable's name like this in the annotation:
@RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
String hello(@PathVariable("name") String name);
Comment From: spencergibb
@ezraroi, @Koizumi85's suggestion is a good workaround. See #841 for work being done to fix this.
Comment From: ezraroi
Looks like it is solving the issue.
I also noticed that if I have @RequestMapping annotation on the interface, the generated client is ignoring it.
@FeignClient("HelloServer")
@RequestMapping(value = "/api")
public interface ITestController {
@RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
String hello(@PathVariable(value = "name") String name);
@RequestMapping(value = "/hello", method = RequestMethod.GET)
String hello();
@RequestMapping(value = "/randomJob", method = RequestMethod.GET)
JobDto getRandomJob();
}
The generated client will not add \api to the requests it is making
Comment From: spencergibb
Putting @RequestMapping on a controller is not currently supported. It causes spring mvc to try and use the feign client as an actual controller.
Comment From: ezraroi
@spencergibb thanks for the quick response
Comment From: cforce
Is there already a request issue to support it?
Comment From: spencergibb
To support what?
Comment From: cforce
Let Mvc detect that a @RequestMapping on (a extended) interface is intensionally used by a @FeignClientto reuse the api contract and not meant to be as own controller instance. Normally thta shall be the case if there is no implementation available for this interface. Its related with https://jira.spring.io/browse/SPR-11055 and https://github.com/spring-projects/spring-framework/pull/976
Comment From: mbenson
The original issue here seems to be the non-defaulted parameter name. This is addressed by #835 .
Comment From: cforce
To support Putting @RequestMapping on a Controller (or some parent in inheritance hierarchy using Scoped interfaces)
Comment From: spencergibb
@cforce that is not this issue.
Comment From: wispycampaign46
@spencergibb what is the mechanism for feignclient annotation to work
Comment From: vbisht21
// you need to add value in path variable as shown below. @FeignClient("HelloServer") public interface ITestController {
@RequestMapping(value = "/hello/{name}", method = RequestMethod.GET)
String hello(@PathVariable(value="name") String name);
@RequestMapping(value = "/hello", method = RequestMethod.GET)
String hello();
@RequestMapping(value = "/randomJob", method = RequestMethod.GET)
JobDto getRandomJob();
Comment From: mychalvlcek
I had similar issue after upgrade to Springboot3.2 + java21 and it was caused by older version of maven-compiler-plugin