SpringCloud Feign etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster
SpringCloud Feign etiketine sahip kayıtlar gösteriliyor. Tüm kayıtları göster

30 Aralık 2022 Cuma

SpringCloud OpenFeign Unit Test

Giriş
1. FakeFeignConfiguration yaratılır. Bu sınıf içinde FakeRibbonConfiguration ayağa kaldırılır. 
2. FakeRibbonConfiguration  da çağrıları @LocalServerPort ile testin kullandığı porta yönlendirir.
3. Test içinde FakeMessagingRestService ayağa kaldırılır

Örnek
Açıklaması şöyle
Spring uses OpenFeign under the hood to build the client component that does the rest-based service call and uses Netflix Ribbon to provide client-side load balancing to the provisioned client component.
Örnek
Elimizde FeignClient kodu olsun
@FeignClient(name = "messagingRestClient", path = "/messaging")
public interface MessagingRestClient {
  @GetMapping(params = {"name"})
  Message getMessage(@RequestParam("name") final String name);

  @PostMapping(params = {"name"})
  Message setMessage(@RequestParam("name") final String name, 
                     @RequestBody final Message message);

}
Şöyle yaparız. FakeFeignConfiguration ve feign çağrılarına cevap verecek FakeMessagingRestService test içinde ayağa kaldırılır. FakeFeignConfiguration içinde localhost'a işaret eden bir Ribbon client ayarı yapılır. Böylece tüm feign istekleri localhost'a gönderilir. RibbonClient içinde com.netflix.loadbalancer.Server ve com.netflix.loadbalancer.ServerList sınıfları kullanılıyor
@SpringBootTest(classes = {MessagingRestClientBootTests.FakeFeignConfiguration.class,
                           MessagingRestClientBootTests.FakeMessagingRestService.class},
                webEnvironment = WebEnvironment.RANDOM_PORT)
class MessagingRestClientBootTests {
  @RestController
  @RequestMapping(path = "/messaging")
  static class FakeMessagingRestService {
    @GetMapping(params = {"name"},produces = "application/json")
    public String getMessage(@RequestParam("name") final String name) {
      assertThat(name).isEqualTo("Foo");
      return "{\"text\":\"Hello, Foo\"}";
    }
    @PostMapping(params = {"name"}, produces = "application/json")
    public String setMessage(@RequestParam("name") final String name,
                             @RequestBody final String message) throws Exception {
      assertThat(name).isEqualTo("Foo");
      JSONAssert.assertEquals(message, "{ \"text\":\"Hello\" }", false);
      return "{\"text\":\"Hi, Foo\"}";
    }
  }
  @Configuration(proxyBeanMethods = false)
  static class FakeRibbonConfiguration {
    @LocalServerPort int port;
    @Bean
    public ServerList<Server> serverList() {
      return new StaticServerList<>(new Server("localhost", port));
    }
  }
  @Configuration(proxyBeanMethods = false)
  @EnableFeignClients(clients = MessagingRestClient.class)
  @EnableAutoConfiguration
  @RibbonClient(name = "messagingRestClient",
      configuration = MessagingRestClientBootTests.FakeRibbonConfiguration.class)
  static class FakeFeignConfiguration {}
}
Aynı sınıfta testleri şöyle yaparız
@SpringBootTest(
    classes = {
      MessagingRestClientBootTests.FakeFeignConfiguration.class,
      MessagingRestClientBootTests.FakeMessagingRestService.class
    },
    webEnvironment = WebEnvironment.RANDOM_PORT)
class MessagingRestClientBootTests {

  @Autowired MessagingRestClient client;

  @Test
  public void getMessage() {
    final Message response = client.getMessage("Foo");
    assertThat(response.getText()).isEqualTo("Hello, Foo");
  }

  @Test
  public void setMessage() {
    final Message message = new Message();
    message.setText("Hello");
    final Message response = client.setMessage("Foo", message);
    assertThat(response.getText()).isEqualTo("Hi, Foo");
  }
  ...
}
Örnek
Elimizde FeignClient kodu olsun
@FeignClient(name = "test")
public interface FeignAPI {
  @RequestMapping(value = "hello")
  String hello();
}
Şöyle yaparız. Burada ilk örnekten farklı olarak RestController ayrı bir sınıf değil, FeignConfig içinde tanımlı. Aslında aynı kapıya çıkıyor.
@SpringBootTest(classes = FeignAPITest.FeignConfig.class, 
  webEnvironment = SpringBootTest.WebEnvironment.RANDOM_PORT)
public class FeignAPITest {
  @Autowired
  FeignAPI feignAPI;
  @Configuration
  static class RibbonConfig {
    @LocalServerPort
    int port;
    @Bean
    public ServerList<Server> serverList() {
      return new StaticServerList<>(new Server("127.0.0.1", port));
    }
  }
  @EnableFeignClients(clients = FeignAPI.class)
  @RestController
  @Configuration
  @EnableAutoConfiguration
  @RibbonClient(name = "test", configuration = FeignAPITest.RibbonConfig.class)
  static class FeignConfig {

    @RequestMapping(value = "hello")
    public String testFeign() {
      return "success";
    }
  }
  @Test
  public void testFeign() {
    assertThat(this.feignAPI.hello()).isEqualTo("success");
  }
}

27 Eylül 2021 Pazartesi

SpringCloud Feign Client @EnableFeignClients Anotasyonu

Giriş
Şu satırı dahil ederiz
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.netflix.feign.EnableFeignClients;
Bu anotasyon @FeignClient anotasyonlarının taranması için gereklidir.

Örnek
Şöyle yaparız
@EnableDiscoveryClient
@EnableFeignClients
@SpringBootApplication
public class EmployeeDashBoardServiceApplication {

  public static void main(String[] args) {
    SpringApplication.run(EmployeeDashBoardServiceApplication.class, args);
 

  @Bean
  public RestTemplate restTemplate(RestTemplateBuilder builder) {
    return builder.build();
  }
}
basePackages Alanı 
Örnek
Şöyle yaparız
@SpringBootApplication
@EnableFeignClients("io.xrio.movies.controller.client")
public class MoviesApplication {
  ...
}
clients Alanı
Örnek
Şöyle yaparız
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.openfeign.EnableFeignClients;

import cz.zpapez.springfeignclients.slack.SlackFeignClient;
import cz.zpapez.springfeignclients.zephyr.ZephyrFeignClient;

@SpringBootApplication
@EnableFeignClients(clients = {
        SlackFeignClient.class,
        ZephyrFeignClient.class
})
public class SpringFeignClientsApplication {

  public static void main(String[] args) {
    SpringApplication.run(SpringFeignClientsApplication.class, args);
  }

}

SpringCloud OpenFeign Kullanımı

Giriş
OpenFeign projesi Netflix tarafından geliştirildi ve daha sonra açık kaynak haline geldi.

OpenFeign Ne İşe Yarar?
Açıklaması şöyle. Yani RestTemplate kullanmaktan daha kolay.
FeignClient is a library for creating REST API clients in a declarative way. So, instead of manually coding clients for remote API and maybe using Springs RestTemplate we declare a client definition and the rest is generated during runtime for use.
Açıklaması şöyle
Feign is a declarative web service client. Instead of writing the client code to call REST Services, use feign at the client side to call webservices.

We just need to declare and annotate the interface while the implementation is auto generated at runtime.

The aim of feign is to connect the client to the REST Services with minimal overhead and code. 

Netflix has stopped supporting Feign and transferred Feign to open-source community . The new project is known as OpenFeign.
Kullanım
1.  @EnableFeignClients kullanılır
2. @FeignClient kullanılır
3. Feign.builder ile özelleştirilerek kullanılır

Feign Client kullanınca bir JDK Proxy üretilir. Konuyu açıklayan bir yazı burada

Maven
Örnek
Şöyle yaparız
<dependency>
  <groupId>org.springframework.cloud</groupId>
  <artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
Gradle
Şöyle yaparız
implementation 'org.springframework.cloud:spring-cloud-starter-openfeign'
Unit Test
Açıklaması şöyle
Spring uses OpenFeign under the hood to build the client component that does the rest-based service call and uses Netflix Ribbon to provide client-side load balancing to the provisioned client component.
Örnek
Elimizde şöyle bir kod olsun
@FeignClient(name = "messagingRestClient", path = "/messaging")
public interface MessagingRestClient {
  @GetMapping(params = {"name"})
  Message getMessage(@RequestParam("name") final String name);

  @PostMapping(params = {"name"})
  Message setMessage(@RequestParam("name") final String name, 
                     @RequestBody final Message message);

}
Şöyle yaparız. FakeFeignConfiguration ve feign çağrılarına cevap verecek FakeMessagingRestService test içinde ayağa kaldırılır. FakeFeignConfiguration içinde localhost'a işaret eden bir Ribbon client ayarı yapılır. Böylece tüm feign istekleri localhost'a gönderilir.
@SpringBootTest(
    classes = {
      MessagingRestClientBootTests.FakeFeignConfiguration.class,
      MessagingRestClientBootTests.FakeMessagingRestService.class
    },
    webEnvironment = WebEnvironment.RANDOM_PORT)
class MessagingRestClientBootTests {

 
  @RestController
  @RequestMapping(path = "/messaging")
  static class FakeMessagingRestService {

    @GetMapping(params = {"name"},produces = "application/json")
    public String getMessage(@RequestParam("name") final String name) {

      assertThat(name).isEqualTo("Foo");

      return "{\"text\":\"Hello, Foo\"}";
    }

    @PostMapping(params = {"name"}, produces = "application/json")
    public String setMessage(@RequestParam("name") final String name,
                             @RequestBody final String message)
        throws Exception {

      assertThat(name).isEqualTo("Foo");
      JSONAssert.assertEquals(message, "{ \"text\":\"Hello\" }", false);

      return "{\"text\":\"Hi, Foo\"}";
    }
  }

  @Configuration(proxyBeanMethods = false)
  static class FakeRibbonConfiguration {

    @LocalServerPort int port;

    @Bean
    public ServerList<Server> serverList() {
      return new StaticServerList<>(new Server("localhost", port));
    }
  }

  @Configuration(proxyBeanMethods = false)
  @EnableFeignClients(clients = MessagingRestClient.class)
  @EnableAutoConfiguration
  @RibbonClient(name = "messagingRestClient",
      configuration = MessagingRestClientBootTests.FakeRibbonConfiguration.class)
  static class FakeFeignConfiguration {}
}
Aynı sınıfta testleri şöyle yaparız
@SpringBootTest(
    classes = {
      MessagingRestClientBootTests.FakeFeignConfiguration.class,
      MessagingRestClientBootTests.FakeMessagingRestService.class
    },
    webEnvironment = WebEnvironment.RANDOM_PORT)
class MessagingRestClientBootTests {

  @Autowired MessagingRestClient client;

  @Test
  public void getMessage() {
    final Message response = client.getMessage("Foo");
    assertThat(response.getText()).isEqualTo("Hello, Foo");
  }

  @Test
  public void setMessage() {
    final Message message = new Message();
    message.setText("Hello");
    final Message response = client.setMessage("Foo", message);
    assertThat(response.getText()).isEqualTo("Hi, Foo");
  }
  ...
}
Logging
Açıklaması şöyle. Burada NormalizedLogger diye başka bir kütüphane var
Standard OpenFeign logger ... logs every header in separated log entries, the body goes into another log entry.

It is very inconvenient to deal with such logs in production especially in multithreaded systems.
Örnek
Şöyle yaparız
feign:
  client:
    config:
      auth:
        logger-level: FULL
Örnek
Şöyle yaparız. Burada her bir Feign Configuration için seviye veriliyor.
@Configuration
public class MyConfiguration {

  //Turn on full logging of OpenFeign clients
  @Bean
  Logger.Level feignLoggerLevel() {
    return Logger.Level.FULL;
  }
}

//application.properties
//This setup prints all requests including headers and also responses that are being sent.
//You can verify proper values in proper authorization headers by reading these logs.

logging.level.cz.zpapez.springfeignclients.zephyr.ZephyrFeignClient: DEBUG
logging.level.cz.zpapez.springfeignclients.slack.SlackFeignClient: DEBUG
Feign Configuration
Feign.builder sınıfı kullanılarak özel Feign Configuration belirtilebilir.  Bu konfigürasyon global veya FeignClient bean'e özel olabilir. FeignClient bean'e özel olması için.  Şöyle yaparız
@FeignClient(name = "...", url = "...", 
  configuration = MyFeignClientConfiguration.class)
public interface MyFeignClient {
  ...
}
Şimdi konfigürasyon seçeneklerine bakalım. Açıklaması şöyle
Under the hood feign comes with some components which are used to make a call to remote endpoints and encode/decode request response.

Client — To make HTTP call feign requires http client. By default openfeign comes with a Default Client. We can override it with ApacheHttpClient, OkHttpClient or ApacheHC5FeignClient . These feign clients are wrapper around a delegate client. For example ApacheHttpClient wraps a httpcomponents httpclient and converts the response to feign response.

Decoder — Something needs to convert the feign Response to the actual type of the feign method’s return type. Decoders are that instruments. By default spring provides an OptionalDecoder which delegates to ResponseEntityDecoder which further delegates to SpringDecoder. We can override it by defining a bean of Decoder .

Encoder — We call feign methods by passing objects to it something needs to convert it to http request body. Encoder does that job. Again By default spring provides SpringEncoder.
Alongside the above components, there is also support for caching, and metrics provided by spring feign starter.

We can create a configuration class and override the defaults for the above components.

If we want to override the default for single components feign accepts configuration arguments which we can use to define custom override for default values.
Retry
Açıklaması şöyle
Feign has baked in support for the retry mechanism. However, by default, it uses Retry.NEVER_RETR . For example, we can create a custom retry which will retry any status code > 400. Below is the code for our CustomRetryer.
Şöyle yaparız
public class CustomRetryer extends Retryer.Default {
  public CustomRetryer(long period, long maxPeriod, int maxAttempts) {
    super(period, maxPeriod, maxAttempts);
  }
  @Override
  public void continueOrPropagate(RetryableException e) {
    log.info("Going to retry for ", e);
    super.continueOrPropagate(e);
  }
  @Override
  public Retryer clone() {
    return new CustomRetryer(5,SECONDS.toMillis(1), 5);
  }
}
Açıklaması şöyle
One important fact is that feign Retry works either on IOException or RetryableException thrown from some errorDecoder . Below is what a custom decoder looks like
Şöyle yaparız
@Bean
public ErrorDecoder errorDecoder(){
  return (methodKey, response) -> {
    byte[] body = {};
    try {
    if (response.body() != null) {
      body = Util.toByteArray(response.body().asInputStream());
    }
    } catch (IOException ignored) { // NOPMD
    }
    FeignException exception = new FeignException.BadRequest(response.reason(), 
	    response.request(), body, response.headers());
    if (response.status() >= 400) {
      return new RetryableException(
        response.status(),
        response.reason(),
        response.request().httpMethod(),
        exception,
        Date.from(Instant.now().plus(15, ChronoUnit.MILLIS)),
        response.request());
    }
    return exception;
  };
}
Support for resiliency
Açıklaması şöyle
One form of resiliency is through retries we saw in the last section. Spring has CircuitBreaker support for feign. It achieves it through a separate Feign builder FeignCircuitBreaker.Builder . The actual implementation of circuitbreaker comes from resilience4j library.
Interceptor
Açıklaması şöyle
Sometimes we want to modify the request by adding some extra information. For example, we may add a header for each request. We can achieve this by using `RequestInterceptor`. For the experiment, I added the below interceptor which populates a header userid.
Şöyle yaparız
@Bean
public RequestInterceptor userIdRequestInterceptor(){
  return (template) -> {
    template.header("userid", "somerandomtext");
  };
}
Client side loadbalancing support
Açıklaması şöyle
From spring boot 2.4.0 feign has integration with spring-cloud-loadbalancer which can fetch client url info from various service discovery providers and make that info available to feign .

Usage of feign simplifies various aspects of making HTTP request. In a typical production environment, we may need to override several components like clients, decoder, errorDecoder etc . Also within the Spring ecosystem feign is nicely integrated with resiliency, load-balancing , metrics etc which makes it an automatic choice when we are working in a microservices architecture.

Anti Pattern
Feign şöyle kullanılmamalı


Feign.builder Sınıfı
Örnek
Elimizde şöyle bir arayüz olsun
public interface CustomFeignClient {

  @RequestLine(value = "GET /sample-endpoint")
  String getResponse();
}
Şöyle yaparız
@Configuration
@AllArgsConstructor
@Import({FeignClientsConfiguration.class})
public class CustomFeignClientConfig {

  private final Encoder encoder;
  private final Decoder decoder;

  @Bean
  public CustomFeignClient customFeignClient() {
    return Feign.builder()
      .requestInterceptor(interceptor -> {
        // Set Auth related headers
        interceptor.header("key", "value");
      })
      .encoder(encoder)
      .decoder(decoder)
      .errorDecoder((methodKey, response) -> {
        val defaultErrorEncoder = new ErrorDecoder.Default();
        // Handle specific exception
        return defaultErrorEncoder.decode(methodKey, response);
      }) // Set the appropriate url
      .target(CustomFeignClient.class, "http://localhost:8080/service-b");
    }
}


10 Ağustos 2021 Salı

SpringCloud Feign Feign Sınıfı

Giriş
SpringCloud Feign projesindeki anotasyonları kullanmadan kütüphaneyi direkt kullanmak mümkün.

Örnek
Elimizde şöyle bir arayüz olsun
@Headers("Accept: application/json")
public interface BookClient {

    @RequestLine("POST")
    @Headers("Content-Type: application/json")
    void create(Book book);

    @RequestLine("GET")
    List<Book> findAll();

    @RequestLine("GET /{id}")
    Book findById(@Param("id") Integer id);

    @RequestLine("DELETE /{id}")
    void remove(@Param("id") Integer id);

    @RequestLine("PUT /{id}")
    @Headers("Content-Type: application/json")
    void update(@Param("id") Integer id, Book book);
}
Şöyle yaparız
@Slf4j
public class FeignTest {
  private static BookClient bookClient;
  private static WireMockServer wireMockServer;
  private final static String FOLDER = "src/test/resources/wiremock";

  @BeforeAll
  public static void setup() {
    bookClient = Feign.builder().client(new OkHttpClient())
      .encoder(new GsonEncoder()).decoder(new GsonDecoder())
      .logger(new Slf4jLogger(BookClient.class)).logLevel(Logger.Level.FULL)
      .target(BookClient.class, "http://localhost:57002/api/books");

    wireMockServer = new WireMockServer(WireMockConfiguration.wireMockConfig()
      .withRootDirectory(FOLDER).port(57002)
      .notifier(new ConsoleNotifier(true)));
      wireMockServer.start();
  }

  @Test
  public void findById() throws Exception {
    wireMockServer.stubFor(get(urlPathEqualTo("/api/books/1"))
      .willReturn(aResponse().withBodyFile("book1.json")));

    Book book = bookClient.findById(1);
    assertThat(book.getAuthor(), equalTo("Orson S. Card"));
    wireMockServer.verify(1, getRequestedFor(urlEqualTo("/api/books/1"))
      .withHeader("Accept", WireMock.equalTo("application/json")));
  }
}

15 Şubat 2021 Pazartesi

SpringCloud Feign Retrier Arayüzü

Giriş
Bir başka örnek burada

Örnek
Elimizde şöyle bir kod olsun.
class Custom implements Retryer {

  private final int maxAttempts;
  private final long backoff;
  int attempt;

  public Custom() {
    this(2000, 3);
  }

  public Custom(long backoff, int maxAttempts) {
    this.backoff = backoff;
    this.maxAttempts = maxAttempts;
    this.attempt = 1;
  }

  public void continueOrPropagate(RetryableException e) {
    if (attempt++ >= maxAttempts) {
      throw e;
    }

    try {
      Thread.sleep(backoff);
    } catch (InterruptedException ignored) {
      Thread.currentThread().interrupt();
    }
  }

  @Override
  public Retryer clone() {
    return new Custom(backoff, maxAttempts);
  }
}
Şöyle yaparız
@Configuration
public class FeignClientConfig {

  @Bean
  public Retryer retryer() {
    return new Custom();
  }
}

24 Aralık 2020 Perşembe

SpringCloud Feign @FeignClient Anotasyonu

Giriş
Şu satırı dahil ederiz
import org.springframework.cloud.netflix.feign.FeignClient;
configuration Alanı
Örnek - Basic Authentication
Şöyle yaparız
import feign.auth.BasicAuthRequestInterceptor;

public class ZephyrFeignClientConfiguration {

  @Value("${zephyr.api.username}")
  private String jiraApiUsername;

  @Value("${zephyr.api.password}")
  private String jiraApiPassword;

  @Bean
  public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
    return new BasicAuthRequestInterceptor(jiraApiUsername, jiraApiPassword);
  }
}

@FeignClient(name = "...", url = "...", 
  configuration = ZephyrFeignClientConfiguration.class)
public interface ZephyrFeignClient {

  @RequestMapping(value = "/execution", method = RequestMethod.GET)
  ExecutionListResponseDto getListOfExecutions(@RequestParam(name = "issueId") 
    String issueId);

}
Açıklaması şöyle
Notice — do not mark the whole class as @Configuration. This would make the BasicAuthRequestInterceptor bean available in the whole Spring context and therefore it would be picked up by other Feign Clients.
Örnek - Token Authentication
Şöyle yaparız
import feign.RequestInterceptor;
import feign.RequestTemplate;

public class SlackFeignClientConfiguration {

  @Value("${slack.app.oauth.accessToken}")
  private String slackOauthAccessToken;

  @Bean
  public RequestInterceptor bearerTokenRequestInterceptor() {
    return new RequestInterceptor() {
      @Override
      public void apply(RequestTemplate template) {
        template.header("Authorization",
                        String.format("Bearer %s", slackOauthAccessToken));
      }
    };
  }
}

@FeignClient(name = "Slack", url = "...", 
  configuration = SlackFeignClientConfiguration.class)
public interface SlackFeignClient {

  @RequestMapping(
    value = "/chat.postMessage",
    method = RequestMethod.POST,
    consumes = "application/json",
    produces = "application/json")
  SlackMessageResponseDto postSlackMessage(@RequestBody(required = true) 
    SlackMessageRequestDto messageRequest);
}
fallback Alanı
Bu alanın çalışması için application.properties dosyasına şu eklenir. Böylece servis çalışmıyorsa fallback metodu çağrılır
feign.circuitbreaker.enabled=true
Örnek
Şöyle yaparız
@FeignClient(name="score-segment", fallback = ScoreSegmentFallback.class)
public interface ScoreSegmentProxy {

    @GetMapping("/score-segment/{idNumber}")
    public ScoreSegmentResponse retrieveExchangeValue(@PathVariable BigInteger idNumber);
}

@Component
public class ScoreSegmentFallback implements ScoreSegmentProxy {

  @Override
  public ScoreSegmentResponse retrieveExchangeValue(BigInteger idNumber) {
    return new ScoreSegmentResponse(BigInteger.ONE);
  }
}
name Alanı
url belirtilmediği için Eureka sunucusundan servisi bulur

Örnek
Şöyle yaparız
import org.springframework.cloud.netflix.feign.FeignClient;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;

import com.example.EmployeeDashBoardService.domain.model.EmployeeInfo;

@FeignClient(name="EmployeeSearch" )//Service Id of EmployeeSerach service
public interface EmployeeServiceProxy {

   @RequestMapping("/employee/find/{id}")
   public EmployeeInfo findById(@PathVariable(value="id") Long id);

   @RequestMapping("/employee/findall")
   public Collection<EmployeeInfo> findAll();

}
Açıklaması şöyle
Feign dynamically generates the implementation of the interface we created, so Feign has to know which service to call beforehand. That's why we need to give a name for the interface, which is the {Service-Id} of EmployeeService. Now, Feign contacts the Eureka server with this Service Id, resolves the actual IP/hostname of the EmployeeService, and calls the URL provided in Request Mapping.
url Alanı
Eğer Eureka kullanmıyorsak, URL ile adresi belirtmek gerekir.

Örnek - application.properties
application.properties şöyle olsun
zephyr:
  api:
    baseUrl: https://jira.my-company.com/jira/rest/zapi/latest
Şöyle yaparız
@FeignClient(name = "Zephyr", url = "${zephyr.api.baseUrl}", 
  configuration = ZephyrFeignClientConfiguration.class)
public interface ZephyrFeignClient {

  @RequestMapping(value = "/execution", method = RequestMethod.GET)
  ExecutionListResponseDto getListOfExecutions(@RequestParam(name = "issueId") 
    String issueId);

}

Örnek - dynamic URL
Şöyle yaparız
import java.net.URI;

@FeignClient(name = "exchangeratelistfeignservice",url = "https://dummy.com")
public interface ExchangeRateListFeignService {
  @GetMapping("")
  ResponseEntity<String> getAllExchangeRates(URI baseUrl);
}

@GetMapping Örnekleri
Örnek
Şöyle yaparız
@FeignClient(name = "jsonplaceholder", url = "https://...")
public interface PostClient {
  @GetMapping
  List<PostDTO> getPosts();
}
Örnek
Şöyle yaparız
@FeignClient(url = "<host:port for serviceB>", name = "serviceB")
public interface ServiceBClient{
  @GetMapping("/hello")
  public ResponseEntity<String> sayHello();
}
Örnek - @PathVariable İle Parametre
Şöyle yaparız
@FeignClient(name = "products", url = "https://dummyjson.com/products")
public interface ProductClient {

  @GetMapping("/{id}")
  Product fetchProduct(@PathVariable int id);
}
@RequestMapping Örnekleri

Örnek - @RequestMapping ile Get
Şöyle yaparız
@FeignClient(name = “external-service”, url = “${external-service.url}”,
configuration = ServiceConfiguration.class) 
public interface ExternalServiceClient
 
  @RequestMapping(method = RequestMethod.GET, value = “/external- data/{time}”,
consumes = “application/json”
  Data load(@PathVariable(“time”) Long time); 
}
Örnek
Şöyle yaparız
@FeignClient(name = "bonus", url = "http://localhost:8081/bonus")
public interface BonusClient {

  @RequestMapping(value = "/register", method = RequestMethod.POST)
  String register();
}

@FeignClient(name = "notification", url = "http://localhost:8082/notification")
public interface NotificationClient {

  @PostMapping("/send")
  String send(@RequestBody NotificationRequest notificationRequest);
}

@RestController
@RequestMapping("/bonus")
public class BonusController {

  @PostMapping("/register")
  String register() {
    ...
  }
}

@RestController
@RequestMapping("/notification")
public class NotificationController {

   @PostMapping("/send")
   String send(@RequestBody NotificationRequest notificationRequest) {
     ...
  }
}
Örnek - RequestMapping + BasicAuthRequestInterceptor
BasicAuthentication kullanarak Get isteği göndermek için şöyle yaparız. Burada Spring MVC'ye aiıt
@DeleteMapping
@GetMapping
@PostMapping
@PutMapping
kullanılabilir.

Parametre geçmek için 
@PathVariable
@RequestHeader
@RequestParam
kullanılabilir.
import org.springframework.cloud.openfeign.FeignClient;

@FeignClient(name = "Zephyr", url = "${zephyr.api.baseUrl}",
configuration = ZephyrFeignClientConfiguration.class)
public interface ZephyrFeignClient {

  @RequestMapping(value = "/execution",method = RequestMethod.GET)
  Foo getListOfExecutions(@RequestParam(name = "issueId") String issueId);

}
Burada url değeri application.yml dosyasında şöyle tanımlı
zephyr:
api: baseUrl: https://jira.my-company.com/jira/rest/zapi/latest
Şöyle yaparız
import feign.auth.BasicAuthRequestInterceptor;

public class ZephyrFeignClientConfiguration {

  @Value("${zephyr.api.username}")
  private String jiraApiUsername;

  @Value("${zephyr.api.password}")
  private String jiraApiPassword;

  @Bean
  public BasicAuthRequestInterceptor basicAuthRequestInterceptor() {
    return new BasicAuthRequestInterceptor(jiraApiUsername, jiraApiPassword);
  }

}
Kullanmak için şöyle yaparız
@Service
@RequiredArgsConstructor
public class MyService {

  private final ZephyrFeignClient zephyrClient;

  public void fetchData() {
    Foo foo = zephyrClient.getListOfExecutions("5112096");
    ...
  }
}