亚洲中字慕日产2020,大陆极品少妇内射AAAAAA,无码av大香线蕉伊人久久,久久精品国产亚洲av麻豆网站

資訊專欄INFORMATION COLUMN

翻譯: Spring Cloud Feign使用文檔

andycall / 800人閱讀

摘要:轉載請注明出處翻譯使用文檔使用諸如和之類的工具來實現(xiàn)或服務的客戶端此外允許你在庫如之上編寫自己的代碼通過自定義解碼器和錯誤處理可以用最小的開銷和最少的代碼將你的代碼關聯(lián)到任何基于文本的接口是通過將注解轉換成模板請求來實現(xiàn)它的功能的可以將請求

轉載請注明出處: 翻譯: Spring Cloud Feign使用文檔

Why Feign and not X?

Feign使用諸如JerseyCXF之類的工具來實現(xiàn)ReSTSOAP服務的java客戶端, 此外, Feign允許你在http庫(如: Apache HC)之上編寫自己的代碼. 通過自定義解碼器(decoders)和錯誤處理(error handing), Feign可以用最小的開銷和最少的代碼將你的代碼關聯(lián)到任何基于文本的http接口(http APIS),

How does Feign work?

Feign是通過將注解(annotations)轉換成模板請求來實現(xiàn)它的功能的, Feign可以將請求參數(shù)直接應用到這些模板上. 盡管Feign只支持基于文本的接口, 但同樣的它能顯著地簡化系統(tǒng)的方方面面, 如請求重放等, 此外, Feign也可以使你的單元測試更加簡單.

Java Version Campatibility

Feign 10.x及以上的版本是基于Java 8構建的, 且應該同樣支持Java 9、10、11, 如果你需要在JDK 6的版本上使用的話, 請使用Feign 9.x版本.

Basics

下面的代碼是適配Retrofit示例的用法:

interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List contributors(@Param("owner") String owner, @Param("repo") String repo);
}

public static class Contributor {
  String login;
  int contributions;
}

public class MyApp {
  public static void main(String... args) {
    GitHub github = Feign.builder()
                         .decoder(new GsonDecoder())
                         .target(GitHub.class, "https://api.github.com");
  
    // Fetch and print a list of the contributors to this library.
    List contributors = github.contributors("OpenFeign", "feign");
    for (Contributor contributor : contributors) {
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }
}
Interface Annotations

Feign的注解定義了接口與底層http客戶端功能之間的約定, 默認情況下各個注解的約定含義如下:

Annotation Interface Target Usage
@RequestLine 接口 定義請求的HttpMethodUriTemplate. 模板中可以使用大括號包圍的表達式({expression}), 表達式的值由@Param對應參數(shù)的注解值提供.
@Param 參數(shù) 定義模板變量, 變量的值應該由名字相對應的表達式提供.
@Headers 方法、Type 定義HeaderTemplate; 使用@Param注解的值解析對應的表達式. 當該注解應用在Type上時, 該模板會被應用到每一個請求上. 當該注解應用在方法上時, 該模板僅會被應用到對應的方法上.
@QueryMap 參數(shù) 將鍵值對類型的Map、POJO展開成地址上的請求參數(shù)(query string)
@HeaderMap 參數(shù) 將鍵值對類型的Map展開成請求頭Http Headers.
@Body 方法 定義與UriTemplateHeaderTemplate類似的模板(Template), 該模板可以使用@Param的注解值解析對應的表達式
Templates and Expressions

Feign支持由URI Template - RFC 6570定義的簡單字符串(Level 1)表達式, 表達式的值從相關方法上對應@Param注解提供, 示例如下:

public interface GitHub {
  
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List getContributors(@Param("owner") String owner, @Param("repo") String repository);
  
  class Contributor {
    String login;
    int contributions;
  }
}

public class MyApp {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                         .decoder(new GsonDecoder())
                         .target(GitHub.class, "https://api.github.com");
    
    /* The owner and repository parameters will be used to expand the owner and repo expressions
     * defined in the RequestLine.
     * 
     * the resulting uri will be https://api.github.com/repos/OpenFeign/feign/contributors
     */
    github.contributors("OpenFeign", "feign");
  }
}

表達式必須使用大括號({})包裹著, 并且支持使用冒號(:)分隔的正則表達式來限定表達式的值. 如限定上述例子的owner參數(shù)的值必須是字母: {owner:[a-zA-Z]*}.

Request Parameter Expansion

RequestLineQueryMap遵循 URI Template - RFC 6570規(guī)范對一級模板(Level 1 templates)的規(guī)定:

未被解析的值將會被忽略.

所有未編碼或者通過@Param注解標記為已編碼(encoded)的字符和變量值都使用pct編碼(pct-encoded).

可以從Advanced Usage一節(jié)查看更多示例.

What about slashes? /

默認情況下, @RequestLine@QueryMap模板不會對正斜杠/進行編碼, 如果需要默認對其進行編碼的話, 可以將@RequestLinedecodeSlash屬性值設置為false.

What about plus? +

根據(jù)URI規(guī)范, +可以使用在URI地址和請求參數(shù)(query segments)這兩個部分上, 然而在請求參數(shù)(query)上對該符號的處理卻有可能不一致, 在一些遺留的系統(tǒng)上, +會被解析成一個空白符(space). 對此, Feign采用現(xiàn)代系統(tǒng)對+的解釋, 不會將+認為是一個空白符(space), 并將請求參數(shù)上的+編碼為%2B.

如果你希望將+當成空白符(space), 那么請直接使用一個空格 或者直接將其編碼為%20.

Custom Expansion

@Param注解有一個可選的參數(shù)expander可以用來控制單個參數(shù)的展開行為(expansion), 該屬性的值必須指向一個實現(xiàn)了Expander接口的類:

public interface Expander {
    String expand(Object value);
}

對該方法的返回值的處理與上述規(guī)則相同, 如果返回值是null或者是一個空字符串, 那么該值會被忽略. 如果返回值不是使用pct編碼(pct-encoded)的, 將會自動轉換成pct編碼. 可以從 Custom @Param Expansion 一節(jié)查看更多示例.

Request Headers Expansion

@HeadersHeaderMap模板對 Request Parameter Expansion 一節(jié)闡述的規(guī)則做以下修改, 并遵循之:

未被解析的值將會被忽略, 但如果解析到一個空的值(empty header value), 那么對應的請求頭會被移除.

不會對請求頭使用pct編碼(pct-encoding).

可以從Headers一節(jié)查看示例.

關于@Param參數(shù)和參數(shù)名需要注意的點

無論是在@RequestLine、@QueryMap、@BodyTemplate還是@Headers上的表達式, 只要表達式內的變量名字相同, 那么它們的值也必然相同. 如下面的例子, contentType的值會同時被解析到請求頭(header)和路徑(path)上:

public interface ContentService {
  @RequestLine("GET /api/documents/{contentType}")
  @Headers("Accept: {contentType}")
  String getDocumentByType(@Param("contentType") String type);
}

當你在設計你的接口的一定要牢記這一點.

Reuqest Body Expansion

Body模板對 Request Parameter Expansion 一節(jié)闡述的規(guī)則做以下修改, 并遵循之:

未被解析的值將會被忽略.

展開的值在被解析到請求體之前不會經(jīng)過Encoder處理.

必須指定Content-Type請求頭, 可以從 Body Templates一節(jié)查看示例.

Customization

你可以在很多地方對Feign進行定制. 比如, 你可以使用Feign.builder()對自定義的組件構建API接口:

interface Bank {
  @RequestLine("POST /account/{id}")
  Account getAccountInfo(@Param("id") String id);
}

public class BankService {
  public static void main(String[] args) {
    Bank bank = Feign.builder().decoder(
        new AccountDecoder())
        .target(Bank.class, "https://api.examplebank.com");
  }
}
Multiple Interfaces

Feign客戶以對使用Target(默認是HardCodedTarget)定義的對象生成多個API接口, 這樣你可以在執(zhí)行前動態(tài)發(fā)現(xiàn)服務或者對請求進行裝飾.

例如, 下面的代碼可以實現(xiàn)為從身份服務中獲取當前url授權令牌(auth token), 然后設置到每個請求上:

public class CloudService {
  public static void main(String[] args) {
    CloudDNS cloudDNS = Feign.builder()
      .target(new CloudIdentityTarget(user, apiKey));
  }
  
  class CloudIdentityTarget extends Target {
    /* implementation of a Target */
  }
}
Examples

Feign包含了GitHub和Wikipedia的客戶端示例代碼, 在實踐中也可以參考這些項目, 尤其是example daemon.

Integrations

Feign在設計上就希望能夠和其他開源項目很好的整合到一起, 我們也很樂于將你喜歡的模塊添加進來.

Gson

Gson包含了和JSON接口相關的編碼(GsonEncoder)、解碼器(GsonDecoder), 將它將它用到Feign.Builder的方式如下:

public class Example {
  public static void main(String[] args) {
    GsonCodec codec = new GsonCodec();
    GitHub github = Feign.builder()
                         .encoder(new GsonEncoder())
                         .decoder(new GsonDecoder())
                         .target(GitHub.class, "https://api.github.com");
  }
}
Jackson

Jackson包含了和JSON接口相關的編碼(JacksonEncoder)、解碼器(JacksonDecoder), 將它將它用到Feign.Builder的方式如下:

public class Example {
  public static void main(String[] args) {
      GitHub github = Feign.builder()
                     .encoder(new JacksonEncoder())
                     .decoder(new JacksonDecoder())
                     .target(GitHub.class, "https://api.github.com");
  }
}
Sax

SaxDecoder提供了可以與普通JVM和Android環(huán)境兼容的方式解析XML文本, 下面的例子展示了如何使用:

public class Example {
  public static void main(String[] args) {
      Api api = Feign.builder()
         .decoder(SAXDecoder.builder()
                            .registerContentHandler(UserIdHandler.class)
                            .build())
         .target(Api.class, "https://apihost");
    }
}
JAXB

JAXB包含了和XML接口相關的編碼器(JAXBEncoder)、解碼器(JAXBEncoder), 將它將它用到Feign.Builder的方式如下:

public class Example {
  public static void main(String[] args) {
    Api api = Feign.builder()
             .encoder(new JAXBEncoder())
             .decoder(new JAXBDecoder())
             .target(Api.class, "https://apihost");
  }
}
JAX-RS

JAXRSContract使用JAX-RS規(guī)范提供的標準覆蓋了對注解的處理, 目前實現(xiàn)的是1.1版的規(guī)范, 示例如下:

interface GitHub {
  @GET @Path("/repos/{owner}/{repo}/contributors")
  List contributors(@PathParam("owner") String owner, @PathParam("repo") String repo);
}

public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                       .contract(new JAXRSContract())
                       .target(GitHub.class, "https://api.github.com");
  }
}
OkHttp

OkHttpClient直接將Feign的http請求直接交由OkHttp處理, 后者實現(xiàn)了SPDY協(xié)議和提供了更好的網(wǎng)絡控制能力.

OkHttp整合到Feign中需要你把OkHttp模塊放到classpath下, 然后做如下配置:

public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                     .client(new OkHttpClient())
                     .target(GitHub.class, "https://api.github.com");
  }
}
Ribbon

RibbonClient會覆蓋Feign客戶端的URL解析, 以實現(xiàn)由Ribbon提供的智能路由和彈性能力.

RibbonFeign整合需要你將url中的主機名(host)部分替換成Ribbon客戶端名. 例如Ribbon客戶端明為myAppProd:

public class Example {
  public static void main(String[] args) {
    MyService api = Feign.builder()
          .client(RibbonClient.create())
          .target(MyService.class, "https://myAppProd");
  }
}
Java 11 Http2

Http2Client直接將Feign的http請求交給Java11 New HTTP/2 Client處理, 后者實現(xiàn)了HTTP/2協(xié)議.

要將New HTTP/2 ClientFeign整合使用, 你需要使用Java SDK 11, 并做如下配置:

GitHub github = Feign.builder()
                     .client(new Http2Client())
                     .target(GitHub.class, "https://api.github.com");
Hystrix

HystrixFeign實現(xiàn)了由Hystrix提供的斷路器功能.

要將HystrixFeign整合, 你需要將Hystrix模塊放到classpath下, 并使用HystrixFeign:

public class Example {
  public static void main(String[] args) {
    MyService api = HystrixFeign.builder().target(MyService.class, "https://myAppProd");
  }
}
SOAP

SOAP包含了XML接口相關的編碼器(SOAPEncoder)、解碼器(SOAPDecoder).

該模塊通過JAXB和SOAPMessage實現(xiàn)了對SOAP Body的編碼和解碼的支持, 通過將SOAPFault包裝秤javax.xml.ws.soap.SOAPFaultException實現(xiàn)了對SOAPFault解碼的功能, 因此, 對于SOAPFault的處理, 你只需要捕獲SOAPFaultException.

使用示例如下:

public class Example {
  public static void main(String[] args) {
    Api api = Feign.builder()
         .encoder(new SOAPEncoder(jaxbFactory))
         .decoder(new SOAPDecoder(jaxbFactory))
         .errorDecoder(new SOAPErrorDecoder())
         .target(MyApi.class, "http://api");
  }
}

如果SOAP Faults的響應使用了表示錯誤的狀態(tài)碼(4xx, 5xx, …)的話, 那么你還需要添加一個SOAPErrorDecoder.

SLF4J

SLF4JModule實現(xiàn)了將Feign的日志重定向到SLF4J, 這允許你很容易的就能使用你想用的日志后端(Logback、Log4J等).

要將SLF4JFeign整合, 你需要將SLF4J模塊和對應的日志后端模塊放到classpath下, 并做如下配置:

public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                     .logger(new Slf4jLogger())
                     .target(GitHub.class, "https://api.github.com");
  }
}
Decoders

Feign.builder()允許你手動指定額外的配置, 如配置如何對響應進行解析.

如果你接口定義的方法的返回值是除了ResponseString、byte[]void之外的類型, 那么你必須配置一個非默認的Decoder.

下面的代碼展示了如何配置使用feign-gson對JSON解碼:

public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                     .decoder(new GsonDecoder())
                     .target(GitHub.class, "https://api.github.com");
  }
}

如果你想在對響應進行解碼之前先對其做處理的話, 你可以使用mapAndDecode方法, 下面的代碼展示了對一個jsonp響應的處理, 在將響應交給JSON解碼器之前, 需要先對jsonp做處理:

public class Example {
  public static void main(String[] args) {
    JsonpApi jsonpApi = Feign.builder()
                         .mapAndDecode((response, type) -> jsopUnwrap(response, type), new GsonDecoder())
                         .target(JsonpApi.class, "https://some-jsonp-api.com");
  }
}
Encoders

將一個請求體發(fā)送到服務器的最簡單的辦法是定義一個POST請求方法, 該方法的參數(shù)類型是Stringbyte[], 且參數(shù)上不帶任何注解, 并且你可能還需要設置Content-Type請求頭(如果沒有的話):

interface LoginClient {
  @RequestLine("POST /")
  @Headers("Content-Type: application/json")
  void login(String content);
}

public class Example {
  public static void main(String[] args) {
    client.login("{"user_name": "denominator", "password": "secret"}");
  }
}

而通過配置Encoder, 你可以發(fā)送一個類型安全的請求體, 下面的例子展示了使用feign-gson擴展來實現(xiàn)編碼:

static class Credentials {
  final String user_name;
  final String password;

  Credentials(String user_name, String password) {
    this.user_name = user_name;
    this.password = password;
  }
}

interface LoginClient {
  @RequestLine("POST /")
  void login(Credentials creds);
}

public class Example {
  public static void main(String[] args) {
    LoginClient client = Feign.builder()
                              .encoder(new GsonEncoder())
                              .target(LoginClient.class, "https://foo.com");
    
    client.login(new Credentials("denominator", "secret"));
  }
}
@Body templates

使用@Body注解的模板會使用@Param注解的值來展開模板內部的表達式, 對于POST請求你可能還需要設置Content-Type請求頭(如果沒有的話):

interface LoginClient {

  @RequestLine("POST /")
  @Headers("Content-Type: application/xml")
  @Body("")
  void xml(@Param("user_name") String user, @Param("password") String password);

  @RequestLine("POST /")
  @Headers("Content-Type: application/json")
  // json curly braces must be escaped!
  @Body("%7B"user_name": "{user_name}", "password": "{password}"%7D")
  void json(@Param("user_name") String user, @Param("password") String password);
}

public class Example {
  public static void main(String[] args) {
    client.xml("denominator", "secret"); // 
    client.json("denominator", "secret"); // {"user_name": "denominator", "password": "secret"}
  }
}
Headers

Feign支持在api上為每個請求設置請求頭, 也支持為每個客戶端的請求設置請求頭, 你可以根據(jù)實際場景進行選擇.

Set headers using apis

對于那些明確需要設置某些請求頭的接口的情況, 適用于將請求頭的定義作為接口的一部分.

靜態(tài)配置的請求頭可以通過在接口上使用@Headers注解設置:

@Headers("Accept: application/json")
interface BaseApi {
  @Headers("Content-Type: application/json")
  @RequestLine("PUT /api/{key}")
  void put(@Param("key") String key, V value);
}

也可以在方法上的@Headers使用變量展開動態(tài)指定請求頭的內容:

public interface Api {
   @RequestLine("POST /")
   @Headers("X-Ping: {token}")
   void post(@Param("token") String token);
}

有時候, 對于同一個接口或客戶端的請求頭, 其鍵和值可能會隨著不同的方法調用而發(fā)生變化, 且不可預知(例如: 自定義元數(shù)據(jù)請求頭字段"x-amz-meta-"或"x-goog-meta-"), 此時可以在接口上聲明一個Map參數(shù), 并使用@HeaderMap注解將Map的內容設置為對應請求的請求頭:

public interface Api {
   @RequestLine("POST /")
   void post(@HeaderMap Map headerMap);
}

上述的幾個方法都可以在接口上指定請求的請求頭, 且不需要在構造時對Feign客戶端做任何的定制.

Setting headers per target

當同一個接口的請求需要針對不同的請求對象(endpoints)配置不同的請求頭, 或者需要對同一個接口的每個請求都定制其請求頭時, 可以在Feign客戶端上使用RequestInterceptorTarget來設置請求頭.

使用RequestInterceptor設置請求頭的例子可以在Request Interceptor一節(jié)中查看示例.

使用Target設置請求頭的示例如下:

  static class DynamicAuthTokenTarget implements Target {
    public DynamicAuthTokenTarget(Class clazz,
                                  UrlAndTokenProvider provider,
                                  ThreadLocal requestIdProvider);
    
    @Override
    public Request apply(RequestTemplate input) {
      TokenIdAndPublicURL urlAndToken = provider.get();
      if (input.url().indexOf("http") != 0) {
        input.insert(0, urlAndToken.publicURL);
      }
      input.header("X-Auth-Token", urlAndToken.tokenId);
      input.header("X-Request-ID", requestIdProvider.get());

      return input.request();
    }
  }
  
  public class Example {
    public static void main(String[] args) {
      Bank bank = Feign.builder()
              .target(new DynamicAuthTokenTarget(Bank.class, provider, requestIdProvider));
    }
  }

上述方法的最終效果取決于你對RequestInterceptorTarget內部的實現(xiàn), 可以通過這種方法對每個Feign客戶端的所有接口調用設置請求頭. 這在一些場景下是非常有用的, 如對每個Feign客戶端的所有請求設置認證令牌authentication token. 這些方法是在接口調用者所在的線程中執(zhí)行的(譯者注: 需要注意線程安全), 因此請求頭的值可以是在調用時根據(jù)上下文動態(tài)地設置. 例如, 可以根據(jù)不同的調用線程, 從ThreadLocal里讀取不同的數(shù)據(jù)設置請求頭.

Advanced usage Base Apis

大多數(shù)情況下服務的接口都遵循相同的約定. Feign使用單繼承的方式來實現(xiàn), 比如下面的例子:

interface BaseAPI {
  @RequestLine("GET /health")
  String health();

  @RequestLine("GET /all")
  List all();
}

你可以通過繼承的方式來擁有BaseAPI的接口, 并實現(xiàn)其他特定的接口:

interface CustomAPI extends BaseAPI {
  @RequestLine("GET /custom")
  String custom();
}

很多時候, 接口對資源的表示也是一致的, 因此, 也可以在基類的接口中使用泛型參數(shù):

@Headers("Accept: application/json")
interface BaseApi {

  @RequestLine("GET /api/{key}")
  V get(@Param("key") String key);

  @RequestLine("GET /api")
  List list();

  @Headers("Content-Type: application/json")
  @RequestLine("PUT /api/{key}")
  void put(@Param("key") String key, V value);
}

interface FooApi extends BaseApi { }

interface BarApi extends BaseApi { }
Logging

你可以通過為Feign客戶端設置Logger來記錄其http日志, 最簡單的實現(xiàn)如下:

public class Example {
  public static void main(String[] args) {
    GitHub github = Feign.builder()
                     .decoder(new GsonDecoder())
                     .logger(new Logger.JavaLogger().appendToFile("logs/http.log"))
                     .logLevel(Logger.Level.FULL)
                     .target(GitHub.class, "https://api.github.com");
  }
}
Request Interceptors

如果你需要跨Feign客戶端對所有請求都做修改, 那么你可以配置RequestInterceptor來實現(xiàn). 例如, 如果你是請求的一個代理, 那么你可能會需要設置X-Forwarded-For請求頭:

static class ForwardedForInterceptor implements RequestInterceptor {
  @Override public void apply(RequestTemplate template) {
    template.header("X-Forwarded-For", "origin.host.com");
  }
}

public class Example {
  public static void main(String[] args) {
    Bank bank = Feign.builder()
                 .decoder(accountDecoder)
                 .requestInterceptor(new ForwardedForInterceptor())
                 .target(Bank.class, "https://api.examplebank.com");
  }
}

另一個常見的使用攔截器的場景是授權, 比如使用內置的BasicAuthRequestInterceptor:

public class Example {
  public static void main(String[] args) {
    Bank bank = Feign.builder()
                 .decoder(accountDecoder)
                 .requestInterceptor(new BasicAuthRequestInterceptor(username, password))
                 .target(Bank.class, "https://api.examplebank.com");
  }
}
Custom @Param Expansion

使用@Param注解的參數(shù)會用其toString()方法展開獲得參數(shù)值, 也可以通過制定一個自定義的Param.Expander來控制. 如對日期的格式化:

public interface Api {
  @RequestLine("GET /?since={date}") Result list(@Param(value = "date", expander = DateToMillis.class) Date date);
}
Dynamic Query Parameters

可以通過對Map類型的參數(shù)加上QueryMap注解, 將Map的內容構造成查詢參數(shù)(query parameters):

public interface Api {
  @RequestLine("GET /find")
  V find(@QueryMap Map queryMap);
}

同樣的, 也可以通過使用QueryMapEncoder實現(xiàn)用POJO對象生成查詢參數(shù)(query parameter):

public interface Api {
  @RequestLine("GET /find")
  V find(@QueryMap CustomPojo customPojo);
}

當用這種方式時, 如果沒有指定一個自定義的QueryMapEncoder, 那么查詢參數(shù)的(query parameter)內容將根據(jù)對象的成員變量生成, 參數(shù)名對應變量名. 下面的例子中, 根據(jù)POJO對象生成的查詢參數(shù)(query parameter)的內容是"/find?name={name}&number={number}", 生成的查詢參數(shù)的順序是不固定的, 按照慣例, 如果POJO對象的某個變量值為null, 那么該變量會被丟棄.

public class CustomPojo {
  private final String name;
  private final int number;

  public CustomPojo (String name, int number) {
    this.name = name;
    this.number = number;
  }
}

設置自定義QueryMapEncoder的方式如下:

public class Example {
  public static void main(String[] args) {
    MyApi myApi = Feign.builder()
                 .queryMapEncoder(new MyCustomQueryMapEncoder())
                 .target(MyApi.class, "https://api.hostname.com");
  }
}

當用@QueryMao注解時, 默認的編碼器(encoder)會對對象的字段使用反射來將其展開成查詢參數(shù)(query string). 如果希望通過對象的getter和setter方法來展開查詢參數(shù)(query string), 請使用BeanQueryMapEncoder:

public class Example {
  public static void main(String[] args) {
    MyApi myApi = Feign.builder()
                 .queryMapEncoder(new BeanQueryMapEncoder())
                 .target(MyApi.class, "https://api.hostname.com");
  }
}
Error Handling

你可以通過在Feign實例構造時注冊一個自定義的ErrorDecoder來實現(xiàn)對非正常響應的控制:

public class Example {
  public static void main(String[] args) {
    MyApi myApi = Feign.builder()
                 .errorDecoder(new MyErrorDecoder())
                 .target(MyApi.class, "https://api.hostname.com");
  }
}

所有HTTP狀態(tài)碼不為2xx的響應都會觸發(fā)ErrorDecoderdecode方法, 在這個方法內你可以對這些響應針對性地拋出異常, 或做其他額外的處理. 如果希望對請求進行重試, 那么可以拋出RetryableException, 該異常會觸發(fā)Retryer.

Retry

默認情況下, Feign會對產(chǎn)生IOException的請求自動重試, 無論使用的是哪種HTTP方法, 都認為IOExcdeption是由短暫的網(wǎng)絡問題產(chǎn)生的. 對ErrorDecoder內拋出的RetryableException也會進行請求重試. 你也可以通在Feign實例構造時設置自定義的Retryer來定制重試行為:

public class Example {
  public static void main(String[] args) {
    MyApi myApi = Feign.builder()
                 .retryer(new MyRetryer())
                 .target(MyApi.class, "https://api.hostname.com");
  }
}

Retryer的實現(xiàn)需要決定一個請求是否應該進行重試, 可以通過continueOrPropagate(RetryableException e)方法的返回值(truefalse)來實現(xiàn). 每個Feign客戶端執(zhí)行時都會構造一個Retryer實例, 這樣的話你可以維護每個請求的重新狀態(tài).

如果最終重試也失敗了, 那么會拋出RetryException, 如果希望拋出導致重試失敗的異常, 可以在構造Feign客戶端時指定exceptionPropagationPolicy()選項.

Static and Default Methods

使用Feign的接口可能是靜態(tài)的或默認的方法(Java 8及以上支持), 這允許Feign客戶端包含一些不適用底層接口定義的邏輯. 例如, 使用靜態(tài)方法可以很輕易地指定通用客戶端構造配置, 使用默認方法可以用于組合查詢或定義默認參數(shù):

interface GitHub {
  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List contributors(@Param("owner") String owner, @Param("repo") String repo);

  @RequestLine("GET /users/{username}/repos?sort={sort}")
  List repos(@Param("username") String owner, @Param("sort") String sort);

  default List repos(String owner) {
    return repos(owner, "full_name");
  }

  /**
   * Lists all contributors for all repos owned by a user.
   */
  default List contributors(String user) {
    MergingContributorList contributors = new MergingContributorList();
    for(Repo repo : this.repos(owner)) {
      contributors.addAll(this.contributors(user, repo.getName()));
    }
    return contributors.mergeResult();
  }

  static GitHub connect() {
    return Feign.builder()
                .decoder(new GsonDecoder())
                .target(GitHub.class, "https://api.github.com");
  }
}

文章版權歸作者所有,未經(jīng)允許請勿轉載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。

轉載請注明本文地址:http://www.ezyhdfw.cn/yun/73455.html

相關文章

  • Spring Cloud 參考文檔(聲明式REST客戶端:Feign

    摘要:繼承支持通過單繼承接口支持樣板,這允許將通用操作分組為方便的基本接口。,記錄基本信息以及請求和響應。例如,類定義參數(shù)和以下客戶端使用注解使用類 聲明式REST客戶端:Feign Feign是一個聲明式的Web服務客戶端,它使編寫Web服務客戶端變得更容易,要使用Feign,請創(chuàng)建一個接口并對其進行注解,它具有可插拔的注解支持,包括Feign注解和JAX-RS注解,F(xiàn)eign還支持可插拔...

    wqj97 評論0 收藏0
  • 拜托!面試請不要再問我Spring Cloud底層原理!

    摘要:不過大多數(shù)講解還停留在對功能使用的層面,其底層的很多原理,很多人可能并不知曉。每個線程池里的線程就僅僅用于請求那個服務。 歡迎關注微信公眾號:石杉的架構筆記(id:shishan100) 每日更新!精品技術文章準時送上! 目錄 一、業(yè)務場景介紹 二、Spring Cloud核心組件:Eureka 三、Spring Cloud核心組件:Feign 四、Spring Cloud核心組件:R...

    wums 評論0 收藏0
  • 拜托!面試請不要再問我Spring Cloud底層原理!

    摘要:不過大多數(shù)講解還停留在對功能使用的層面,其底層的很多原理,很多人可能并不知曉。每個線程池里的線程就僅僅用于請求那個服務。 歡迎關注微信公眾號:石杉的架構筆記(id:shishan100) 每日更新!精品技術文章準時送上! 目錄 一、業(yè)務場景介紹 二、Spring Cloud核心組件:Eureka 三、Spring Cloud核心組件:Feign 四、Spring Cloud核心組件:R...

    wangjuntytl 評論0 收藏0
  • SpringCloud核心教程 | 第三篇:服務注冊與發(fā)現(xiàn) Eureka篇

    摘要:下一篇介紹基于的服務注冊與調用。服務提供者工程配置這里服務提供者是使用之前進階教程第三篇整合連接池以及監(jiān)控改造而來,這里一樣的部分就不再重復說明,下面將說明新增的部分。 Spring Cloud簡介 Spring Cloud是一個基于Spring Boot實現(xiàn)的云應用開發(fā)工具,它為基于JVM的云應用開發(fā)中涉及的配置管理、服務發(fā)現(xiàn)、斷路器、智能路由、微代理、控制總線、全局鎖、決策競選、分...

    scq000 評論0 收藏0

發(fā)表評論

0條評論

最新活動
閱讀需要支付1元查看
<