• 设为首页
  • 点击收藏
  • 手机版
    手机扫一扫访问
    迪恩网络手机版
  • 关注官方公众号
    微信扫一扫关注
    迪恩网络公众号

OpenFeign/feign: Feign makes writing java http clients easier

原作者: [db:作者] 来自: 网络 收藏 邀请

开源软件名称:

OpenFeign/feign

开源软件地址:

https://github.com/OpenFeign/feign

开源编程语言:

Java 99.7%

开源软件介绍:

Feign makes writing java http clients easier

Join the chat at https://gitter.im/OpenFeign/feign CircleCI Maven Central

Feign is a Java to HTTP client binder inspired by Retrofit, JAXRS-2.0, and WebSocket. Feign's first goal was reducing the complexity of binding Denominator uniformly to HTTP APIs regardless of ReSTfulness.


Why Feign and not X?

Feign uses tools like Jersey and CXF to write java clients for ReST or SOAP services. Furthermore, Feign allows you to write your own code on top of http libraries such as Apache HC. Feign connects your code to http APIs with minimal overhead and code via customizable decoders and error handling, which can be written to any text-based http API.

How does Feign work?

Feign works by processing annotations into a templatized request. Arguments are applied to these templates in a straightforward fashion before output. Although Feign is limited to supporting text-based APIs, it dramatically simplifies system aspects such as replaying requests. Furthermore, Feign makes it easy to unit test your conversions knowing this.

Java Version Compatibility

Feign 10.x and above are built on Java 8 and should work on Java 9, 10, and 11. For those that need JDK 6 compatibility, please use Feign 9.x

Feature overview

This is a map with current key features provided by feign:

MindMap overview

Roadmap

Feign 11 and beyond

Making API clients easier

Short Term - What we're working on now.

  • Response Caching
    • Support caching of api responses. Allow for users to define under what conditions a response is eligible for caching and what type of caching mechanism should be used.
    • Support in-memory caching and external cache implementations (EhCache, Google, Spring, etc...)
  • Complete URI Template expression support
  • Logger API refactor
    • Refactor the Logger API to adhere closer to frameworks like SLF4J providing a common mental model for logging within Feign. This model will be used by Feign itself throughout and provide clearer direction on how the Logger will be used.
  • Retry API refactor
    • Refactor the Retry API to support user-supplied conditions and better control over back-off policies. This may result in non-backward-compatible breaking changes

Medium Term - What's up next.

  • Async execution support via CompletableFuture
    • Allow for Future chaining and executor management for the request/response lifecycle. Implementation will require non-backward-compatible breaking changes. However this feature is required before Reactive execution can be considered.
  • Reactive execution support via Reactive Streams
    • For JDK 9+, consider a native implementation that uses java.util.concurrent.Flow.
    • Support for Project Reactor and RxJava 2+ implementations on JDK 8.

Long Term - The future ☁️

  • Additional Circuit Breaker Support.
    • Support additional Circuit Breaker implementations like Resilience4J and Spring Circuit Breaker

Basics

Usage typically looks like this, an adaptation of the canonical Retrofit sample.

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

  @RequestLine("POST /repos/{owner}/{repo}/issues")
  void createIssue(Issue issue, @Param("owner") String owner, @Param("repo") String repo);

}

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

public static class Issue {
  String title;
  String body;
  List<String> assignees;
  int milestone;
  List<String> labels;
}

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<Contributor> contributors = github.contributors("OpenFeign", "feign");
    for (Contributor contributor : contributors) {
      System.out.println(contributor.login + " (" + contributor.contributions + ")");
    }
  }
}

Interface Annotations

Feign annotations define the Contract between the interface and how the underlying client should work. Feign's default contract defines the following annotations:

Annotation Interface Target Usage
@RequestLine Method Defines the HttpMethod and UriTemplate for request. Expressions, values wrapped in curly-braces {expression} are resolved using their corresponding @Param annotated parameters.
@Param Parameter Defines a template variable, whose value will be used to resolve the corresponding template Expression, by name provided as annotation value. If value is missing it will try to get the name from bytecode method parameter name (if the code was compiled with -parameters flag).
@Headers Method, Type Defines a HeaderTemplate; a variation on a UriTemplate. that uses @Param annotated values to resolve the corresponding Expressions. When used on a Type, the template will be applied to every request. When used on a Method, the template will apply only to the annotated method.
@QueryMap Parameter Defines a Map of name-value pairs, or POJO, to expand into a query string.
@HeaderMap Parameter Defines a Map of name-value pairs, to expand into Http Headers
@Body Method Defines a Template, similar to a UriTemplate and HeaderTemplate, that uses @Param annotated values to resolve the corresponding Expressions.

Overriding the Request Line

If there is a need to target a request to a different host then the one supplied when the Feign client was created, or you want to supply a target host for each request, include a java.net.URI parameter and Feign will use that value as the request target.

@RequestLine("POST /repos/{owner}/{repo}/issues")
void createIssue(URI host, Issue issue, @Param("owner") String owner, @Param("repo") String repo);

Templates and Expressions

Feign Expressions represent Simple String Expressions (Level 1) as defined by URI Template - RFC 6570. Expressions are expanded using their corresponding Param annotated method parameters.

Example

public interface GitHub {

  @RequestLine("GET /repos/{owner}/{repo}/contributors")
  List<Contributor> contributors(@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");
  }
}

Expressions must be enclosed in curly braces {} and may contain regular expression patterns, separated by a colon : to restrict resolved values. Example owner must be alphabetic. {owner:[a-zA-Z]*}

Request Parameter Expansion

RequestLine and QueryMap templates follow the URI Template - RFC 6570 specification for Level 1 templates, which specifies the following:

  • Unresolved expressions are omitted.
  • All literals and variable values are pct-encoded, if not already encoded or marked encoded via a @Param annotation.

We also have limited support for Level 3, Path Style Expressions, with the following restrictions:

  • Maps and Lists are expanded by default.
  • Only Single variable templates are supported.

Examples:

{;who}             ;who=fred
{;half}            ;half=50%25
{;empty}           ;empty
{;list}            ;list=red;list=green;list=blue
{;map}             ;semi=%3B;dot=.;comma=%2C
public interface MatrixService {

  @RequestLine("GET /repos{;owners}")
  List<Contributor> contributors(@Param("owners") List<String> owners);

  class Contributor {
    String login;
    int contributions;
  }
}

If owners in the above example is defined as Matt, Jeff, Susan, the uri will expand to /repos;owners=Matt;owners=Jeff;owners=Susan

For more information see RFC 6570, Section 3.2.7

Undefined vs. Empty Values

Undefined expressions are expressions where the value for the expression is an explicit null or no value is provided. Per URI Template - RFC 6570, it is possible to provide an empty value for an expression. When Feign resolves an expression, it first determines if the value is defined, if it is then the query parameter will remain. If the expression is undefined, the query parameter is removed. See below for a complete breakdown.

Empty String

public void test() {
   Map<String, Object> parameters = new LinkedHashMap<>();
   parameters.put("param", "");
   this.demoClient.test(parameters);
}

Result

http://localhost:8080/test?param=

Missing

public void test() {
   Map<String, Object> parameters = new LinkedHashMap<>();
   this.demoClient.test(parameters);
}

Result

http://localhost:8080/test

Undefined

public void test() {
   Map<String, Object> parameters = new LinkedHashMap<>();
   parameters.put("param", null);
   this.demoClient.test(parameters);
}

Result

http://localhost:8080/test

See Advanced Usage for more examples.

What about slashes? /

@RequestLine templates do not encode slash / characters by default. To change this behavior, set the decodeSlash property on the @RequestLine to false.

What about plus? +

Per the URI specification, a + sign is allowed in both the path and query segments of a URI, however, handling of the symbol on the query can be inconsistent. In some legacy systems, the + is equivalent to the a space. Feign takes the approach of modern systems, where a + symbol should not represent a space and is explicitly encoded as %2B when found on a query string.

If you wish to use + as a space, then use the literal character or encode the value directly as %20

Custom Expansion

The @Param annotation has an optional property expander allowing for complete control over the individual parameter's expansion. The expander property must reference a class that implements the Expander interface:

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

The result of this method adheres to the same rules stated above. If the result is null or an empty string, the value is omitted. If the value is not pct-encoded, it will be. See Custom @Param Expansion for more examples.

Request Headers Expansion

Headers and HeaderMap templates follow the same rules as Request Parameter Expansion with the following alterations:

  • Unresolved expressions are omitted. If the result is an empty header value, the entire header is removed.
  • No pct-encoding is performed.

See Headers for examples.

A Note on @Param parameters and their names:

All expressions with the same name, regardless of their position on the @RequestLine, @QueryMap, @BodyTemplate, or @Headers will resolve to the same value. In the following example, the value of contentType, will be used to resolve both the header and path expression:

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

Keep this in mind when designing your interfaces.

Request Body Expansion

Body templates follow the same rules as Request Parameter Expansion with the following alterations:

  • Unresolved expressions are omitted.
  • Expanded value will not be passed through an Encoder before being placed on the request body.
  • A Content-Type header must be specified. See Body Templates for examples.

Customization

Feign has several aspects that can be customized.
For simple cases, you can use Feign.builder() to construct an API interface with your custom components.
For request setting, you can use options(Request.Options options) on target() to set connectTimeout, connectTimeoutUnit, readTimeout, readTimeoutUnit, followRedirects.
For example:

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())
        .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true))
        .target(Bank.class, "https://api.examplebank.com");
  }
}

Multiple Interfaces

Feign can produce multiple api interfaces. These are defined as Target<T> (default HardCodedTarget<T>), which allow for dynamic discovery and decoration of requests prior to execution.

For example, the following pattern might decorate each request with the current url and auth token from the identity service.

public class CloudService {
  public static void main(String[] args) {
    CloudDNS cloudDNS = Feign.builder()
      .target(new CloudIdentityTarget<CloudDNS>(user, apiKey));
  }

  class CloudIdentityTarget extends Target<CloudDNS> {
    /* implementation of a Target */
  }
}

Examples

Feign includes example GitHub and Wikipedia clients. The denominator project can also be scraped for Feign in practice. Particularly, look at its example daemon.


Integrations

Feign intends to work well with other Open Source tools. Modules are welcome to integrate with your favorite projects!

Gson

Gson includes an encoder and decoder you can use with a JSON API.

Add GsonEncoder and/or GsonDecoder to your Feign.Builder like so:

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 includes an encoder and decoder you can use with a JSON API.

Add JacksonEncoder and/or JacksonDecoder to your Feign.Builder like so:

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");
  }
}

For the lighter weight Jackson Jr, use JacksonJrEncoder and JacksonJrDecoder from the Jackson Jr Module.

Sax

SaxDecoder allows you to decode XML in a way that is compatible with normal JVM and also Android environments.

Here's an example of how to configure Sax response parsing:

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 includes an encoder and decoder you can use with an XML API.

Add JAXBEncoder and/or JAXBDecoder to your Feign.Builder like so:

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 overrides annotation processing to instead use standard ones supplied by the JAX-RS specification. This is currently targeted at the 1.1 spec.

Here's the example above re-written to use JAX-RS:

interface GitHub {
  @GET @Path("/repos/{owner}/{repo}/contributors")
  List<Contributor> 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 directs Feign's http requests to OkHttp, which enables SPDY and better network control.

To use OkHttp with Feign, add the OkHttp module to your classpath. Then, configure Feign to use the OkHttpClient:

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


鲜花

握手

雷人

路过

鸡蛋
该文章已有0人参与评论

请发表评论

全部评论

专题导读
热门推荐
阅读排行榜

扫描微信二维码

查看手机版网站

随时了解更新最新资讯

139-2527-9053

在线客服(服务时间 9:00~18:00)

在线QQ客服
地址:深圳市南山区西丽大学城创智工业园
电邮:jeky_zhao#qq.com
移动电话:139-2527-9053

Powered by 互联科技 X3.4© 2001-2213 极客世界.|Sitemap