Использование @RequestLine с Feign

У меня есть рабочий интерфейс Feign, определенный как:

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

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

}

Если я изменю это, чтобы использовать @RequestLine

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

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

}

Я получаю исключение

Вызывается: java.lang.IllegalStateException: метод getLinksForTrack не аннотирован типом метода HTTP (например, GET, POST)

Есть идеи почему?

4 ответа

Решение

Я не ожидал бы, что это сработает.

@RequestLine является основной аннотацией Feign, но вы используете Spring Cloud @FeignClient который использует аннотации Spring MVC.

Весна создала свой собственный Feign Contract чтобы позволить вам использовать Spring's @RequestMapping аннотации вместо Feigns. Вы можете отключить это поведение, включив бин типа feign.Contract.Default в контексте вашего приложения.

Если вы используете spring-boot (или что-нибудь, использующее конфигурацию Java), включая это в @Configuration класс должен снова включить аннотации Feign:

@Bean
public Contract useFeignAnnotations() {
    return new Contract.Default();
}

Причина, по которой я получил эту ошибку, заключается в том, что я использовал оба@FeignClientи@RequestLineаннотации в моемFooClientинтерфейс.

Перед исправлением.

      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
}

Затем я получил эту ошибку.

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

После исправления

      // 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
}

Если вас интересуют классы конфигурации.

  • Обратите внимание, что я пытался создать Feign Clients вручную.
      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;
    }
}

Рекомендации

  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. /questions/1269037/ispolzovanie-requestline-s-feign/1269048#1269048

Твой @RequestMapping значение выглядит нормально, но вам, вероятно, стоит подумать о его небольшом переписывании:

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

Между прочим, мне не удалось получить @RequestLine работать из-за той же ошибки, что и ваша.

Также для @ReactiveFeignClients Contract.Default() приводит к следующим ошибкам:

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

и должен быть исправлен следующим образом:

var MyClient = WebReactiveFeign.builder()
        .contract(new ReactiveContract(new SpringMvcContract()))
        .target(MyClient, "http://example.com")
Другие вопросы по тегам