Feign makes writing java http clients easier
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

188 lines
8.5 KiB

11 years ago
# Feign makes writing java http clients easier
Feign is a java to http client binder inspired by [Dagger](https://github.com/square/dagger), [Retrofit](https://github.com/square/retrofit), [JAXRS-2.0](https://jax-rs-spec.java.net/nonav/2.0/apidocs/index.html), and [WebSocket](http://www.oracle.com/technetwork/articles/java/jsr356-1937161.html). Feign's first goal was reducing the complexity of binding [Denominator](https://github.com/Netflix/Denominator) uniformly to http apis regardless of [restfulness](http://www.slideshare.net/adrianfcole/99problems).
11 years ago
### Why Feign and not X?
You can use tools like Jersey and CXF to write java clients for ReST or SOAP services. You can write your own code on top of http transport libraries like Apache HC. Feign aims to connect your code to http apis with minimal overhead and code. Via customizable decoders and error handling, you should be able to write to any text-based http api.
### How does Feign work?
Feign works by processing annotations into a templatized request. Just before sending it off, arguments are applied to these templates in a straightforward fashion. While this limits Feign to only supporting text-based apis, it dramatically simplified system aspects such as replaying requests. It is also stupid easy to unit test your conversions knowing this.
### Basics
Usage typically looks like this, an adaptation of the [canonical Retrofit sample](https://github.com/square/retrofit/blob/master/retrofit-samples/github-client/src/main/java/com/example/retrofit/GitHubClient.java).
```java
interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Named("owner") String owner, @Named("repo") String repo);
11 years ago
}
static class Contributor {
String login;
int contributions;
}
public static void main(String... args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
11 years ago
// Fetch and print a list of the contributors to this library.
List<Contributor> contributors = github.contributors("netflix", "feign");
for (Contributor contributor : contributors) {
System.out.println(contributor.login + " (" + contributor.contributions + ")");
}
}
```
### 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 example:
```java
interface Bank {
@RequestLine("POST /account/{id}")
Account getAccountInfo(@Named("id") String id);
}
...
Bank bank = Feign.builder().decoder(new AccountDecoder()).target(Bank.class, "https://api.examplebank.com");
```
For further flexibility, you can use Dagger modules directly. See the `Dagger` section for more details.
### Request Interceptors
When you need to change all requests, regardless of their target, you'll want to configure a `RequestInterceptor`.
For example, if you are acting as an intermediary, you might want to propagate the `X-Forwarded-For` header.
```java
static class ForwardedForInterceptor implements RequestInterceptor {
@Override public void apply(RequestTemplate template) {
template.header("X-Forwarded-For", "origin.host.com");
}
}
...
Bank bank = Feign.builder().decoder(accountDecoder).requestInterceptor(new ForwardedForInterceptor()).target(Bank.class, "https://api.examplebank.com");
```
Another common example of an interceptor would be authentication, such as using the built-in `BasicAuthRequestInterceptor`.
```java
Bank bank = Feign.builder().decoder(accountDecoder).requestInterceptor(new BasicAuthRequestInterceptor(username, password)).target(Bank.class, "https://api.examplebank.com");
```
11 years ago
### 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.
```java
CloudDNS cloudDNS = Feign.builder().target(new CloudIdentityTarget<CloudDNS>(user, apiKey));
11 years ago
```
You can find [several examples](https://github.com/Netflix/feign/tree/master/core/src/test/java/feign/examples) in the test tree. Do take time to look at them, as seeing is believing!
### Integrations
Feign intends to work well within Netflix and other Open Source communities. Modules are welcome to integrate with your favorite projects!
### Gson
[GsonModule](https://github.com/Netflix/feign/tree/master/gson) adds default encoders and decoders so you get get started with a JSON api.
Add `GsonEncoder` and/or `GsonDecoder` to your `Feign.Builder` like so:
```java
GsonCodec codec = new GsonCodec();
GitHub github = Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
```
Squashed commit of the following: commit 34eb5751c760cf1f11cdbab920d6a3a1c6f06640 Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 15 15:54:20 2013 -0400 Remove unnecessary defensive close of Reader commit 38e51606750517d4a52571c408190e614a4a4834 Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 13:59:35 2013 -0400 Replace wildcard import with individual imports commit cc845814ea677ba5920caf5a7a914010623caf1e Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 13:55:37 2013 -0400 Revert GitHub example to use JacksonDecoder rather than JacksonModule now that JacksonDecoder behaves sensibly with its default ObjectMapper commit 8b9638261afe2549c3a43238ee1b66d044f969f4 Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 13:52:45 2013 -0400 Configure default ObjectMapper used by JacksonEncoder and JacksonDecoder with sensible overrides of default behaviors commit 0f275bf7574b66c20a0e6aefe0140f599638992f Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 13:18:26 2013 -0400 Unwrap RuntimeJsonMappingExceptions caught in JacksonDecoder, since they are only ever used to wrap JsonMappingExceptions, which are IOExceptions. commit 1b6995260a5727796e388bbb0b6c88b65e182415 Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 13:09:44 2013 -0400 Update Jackson integration README commit add4007a59559e7b4e2accfa0b0a0215bab62cef Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 13:07:35 2013 -0400 Update CHANGES and README to reflect addition of Jackson integration commit 86c0fcfc704b1b8d03e5eaf69c608fc2761d617b Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 12:11:56 2013 -0400 Update Jackson GitHub example to make use of JacksonModule, and to avoid the need for Jackson annotations commit 1552b3f8239636da0f27ace3c7b42038536e5caf Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 12:05:56 2013 -0400 Replace wildcard import with individual imports commit 0b7cfd08516dfbf66f1a69263ed456f2c0671c76 Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 11:01:11 2013 -0400 Initial implementation of Jackson codec This new codec may be used as an alternative to Gson. commit 94027ec3319f5145c0e18ef472d8e928e97a9527 Author: Matt Hurne <matt@thehurnes.com> Date: Tue Oct 8 08:31:14 2013 -0400 Improve EncodeException and DecodeException Javadoc comments
11 years ago
### Jackson
[JacksonModule](https://github.com/Netflix/feign/tree/master/jackson) adds an encoder and decoder you can use with a JSON API.
Add `JacksonEncoder` and/or `JacksonDecoder` to your `Feign.Builder` like so:
```java
GitHub github = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.target(GitHub.class, "https://api.github.com");
```
### Sax
[SaxDecoder](https://github.com/Netflix/feign/tree/master/sax) 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:
```java
api = Feign.builder()
.decoder(SAXDecoder.builder()
.registerContentHandler(UserIdHandler.class)
.build())
.target(Api.class, "https://apihost");
```
### JAX-RS
[JAXRSModule](https://github.com/Netflix/feign/tree/master/jaxrs) 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:
```java
interface GitHub {
@GET @Path("/repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@PathParam("owner") String owner, @PathParam("repo") String repo);
}
```
### Ribbon
[RibbonModule](https://github.com/Netflix/feign/tree/master/ribbon) overrides URL resolution of Feign's client, adding smart routing and resiliency capabilities provided by [Ribbon](https://github.com/Netflix/ribbon).
Integration requires you to pass your ribbon client name as the host part of the url, for example `myAppProd`.
```java
MyService api = Feign.create(MyService.class, "https://myAppProd", new RibbonModule());
```
### Decoders
`Feign.builder()` allows you to specify additional configuration such as how to decode a response.
If any methods in your interface return types besides `Response`, `String` or `void`, you'll need to configure a `Decoder`.
Here's how to configure json decoding (using the `feign-gson` extension):
```java
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
```
11 years ago
### Advanced usage and Dagger
#### Dagger
Feign can be directly wired into Dagger which keeps things at compile time and Android friendly. As opposed to exposing builders for config, Feign intends users to embed their config in Dagger.
Where possible, Feign configuration uses normal Dagger conventions. For example, `RequestInterceptor` bindings are of `Provider.Type.SET`, meaning you can have multiple interceptors. Here's an example of multiple interceptor bindings.
11 years ago
```java
@Provides(type = SET) RequestInterceptor forwardedForInterceptor() {
return new RequestInterceptor() {
@Override public void apply(RequestTemplate template) {
template.header("X-Forwarded-For", "origin.host.com");
}
};
}
@Provides(type = SET) RequestInterceptor userAgentInterceptor() {
return new RequestInterceptor() {
@Override public void apply(RequestTemplate template) {
template.header("User-Agent", "My Cool Client");
}
};
11 years ago
}
```
#### Logging
You can log the http messages going to and from the target by setting up a `Logger`. Here's the easiest way to do that:
11 years ago
```java
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");
11 years ago
```