24

I have a working Feign interface defined as:

@FeignClient("content-link-service")
public interface ContentLinkServiceClient {

    @RequestMapping(method = RequestMethod.GET, value = "/{trackid}/links")
    List<Link> getLinksForTrack(@PathVariable("trackid") Long trackId);

}

If I change this to use @RequestLine

@FeignClient("content-link-service")
public interface ContentLinkServiceClient {

    @RequestLine("GET /{trackid}/links")
    List<Link> getLinksForTrack(@Param("trackid") Long trackId);

}

I get the exception

Caused by: java.lang.IllegalStateException: Method getLinksForTrack not annotated with HTTP method type (ex. GET, POST)

Any ideas why?

nickcodefresh
  • 887
  • 2
  • 10
  • 25

4 Answers4

31

I wouldn't expect this to work.

@RequestLine is a core Feign annotation, but you are using the Spring Cloud @FeignClient which uses Spring MVC annotations.

Alex Wittig
  • 2,800
  • 1
  • 33
  • 42
22

Spring has created their own Feign Contract to allow you to use Spring's @RequestMapping annotations instead of Feigns. You can disable this behavior by including a bean of type feign.Contract.Default in your application context.

If you're using spring-boot (or anything using Java config), including this in an @Configuration class should re-enable Feign's annotations:

@Bean
public Contract useFeignAnnotations() {
    return new Contract.Default();
}
Michael K
  • 3,297
  • 3
  • 25
  • 37
  • 2
    And where exactly in the code we should call useFeignAnnotations(). Or is there an internal bean that will read useFeignAnnotations()? I have this problem and added the bean in my config, but as expected it does nothing – Alex Dec 03 '20 at 17:00
  • I'm also curious about this^ – frlzjosh Jul 19 '21 at 18:22
2

The reason I got this error is that I used both @FeignClient and @RequestLine annotations in my FooClient interface.

Before a fix.

import org.springframework.cloud.openfeign.FeignClient; // @FeignClient
import feign.RequestLine; //  @RequestLine
import org.springframework.web.bind.annotation.PathVariable;

@FeignClient("foo")
public interface FooClient {

    @RequestLine("GET /api/v1/foos/{fooId}")
    @Headers("Content-Type: application/json")
    ResponseEntity getFooById(@PathVariable("fooId") Long fooId); // I mistakenly used @PathVariable annotation here, but this should be @Param
}

Then, I got this error.

Caused by: java.lang.IllegalStateException: Method FooClient#getFooById(Long) not annotated with HTTP method type (ex. GET, POST)

After a fix

// removed @FeignClient
// removed @PathVariable
import feign.Param; // Added
import feign.RequestLine; //  @RequestLine

// removed @FeignClient("foo")
public interface FooClient {

    @RequestLine("GET /api/v1/foos/{fooId}")
    @Headers("Content-Type: application/json")
    Foo getFooById(@Param("fooId") Long fooId); // used @Param
}

If you are interested in the configuration classes.

  • Please note that I tried to create Feign Clients Manually.
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.ComponentScan;
import org.springframework.context.annotation.ComponentScans;
import org.springframework.context.annotation.Configuration;

@Configuration
@ComponentScans(value = {
        @ComponentScan(basePackages = {
                "com.example.app.service.web.client",
        })
})
public class FeignConfig {
    @Value(value = "${app.foo.service.client.url}")
    protected String url; // http://localhost:8081/app

    @Bean
    public FooClient fooClient() {
        FooClient fooClient = Feign.builder()
//                .client(RibbonClient.create())
                .client(new OkHttpClient())
                .encoder(new GsonEncoder())
                .decoder(new GsonDecoder())
                .logger(new Slf4jLogger(FooClient.class))
                .logLevel(Logger.Level.FULL)
                .target(FooClient.class, url);
        return fooClient;
    }
}

References

  1. https://cloud.spring.io/spring-cloud-netflix/multi/multi_spring-cloud-feign.html
  2. https://www.baeldung.com/intro-to-feign
  3. https://www.baeldung.com/feign-requestline
  4. https://stackoverflow.com/a/32488372/12898581
Dhanusha_Perera07
  • 3,347
  • 5
  • 14
  • 22
-1

Your @RequestMapping value looks ok, but you're likely should consider slightly rewriting it:

 @GetMapping(value = "/{trackid}/links")
 List<Link> getLinksForTrack(@PathVariable(name = "trackid") Long trackId);

Btw I did not succeeded with getting @RequestLine to work due to same error as yours.

Also for @ReactiveFeignClients Contract.Default() yields to following errors:

java.lang.IllegalStateException: Method MyClient#doStuff(String,String) not annotated with HTTP method type (ex. GET, POST)
Warnings:
- Class MyClient has annotations [Component, ReactiveFeignClient, Metadata] that are not used by contract Default
- Method doStuff has an annotation GetMapping that is not used by contract Default

and should be fixed like:

var MyClient = WebReactiveFeign.builder()
        .contract(new ReactiveContract(new SpringMvcContract()))
        .target(MyClient, "http://example.com")
im_infamous
  • 972
  • 1
  • 17
  • 29