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.
 
 
adriancole b389ef03ba support Feign.builder() w/ SAX decoder 11 years ago
codequality Update codequality/checkstyle.xml 12 years ago
core/src Moved SaxDecoder into feign-sax dependency. 11 years ago
example-github updated examples to 4.3 syntax 11 years ago
example-wikipedia updated examples to 4.3 syntax 11 years ago
gradle Fixing aggregateJavadoc 12 years ago
gson ensure gson does not attempt to decode void 11 years ago
jaxrs Remove support for Observable methods. 11 years ago
ribbon provide encoder/decoder in a module that addsTo = Feign.Defaults.class 11 years ago
sax support Feign.builder() w/ SAX decoder 11 years ago
.gitignore added dagger IDE setup for annotation parsing via gradle idea and eclipse plugins 11 years ago
CHANGES.md Moved SaxDecoder into feign-sax dependency. 11 years ago
LICENSE Restructure into smaller files 13 years ago
NOTICE default client: use custom HostnameVerifier if overridden 11 years ago
README.md support Feign.builder() w/ SAX decoder 11 years ago
build.gradle Moved SaxDecoder into feign-sax dependency. 11 years ago
dagger.gradle added dagger IDE setup for annotation parsing via gradle idea and eclipse plugins 11 years ago
gradle.properties bump to 5.0.0-SNAPSHOT 11 years ago
gradlew Upgrading to Gradle 1.4 12 years ago
gradlew.bat Upgrade to Gradle 1.1. 12 years ago
settings.gradle Moved SaxDecoder into feign-sax dependency. 11 years ago

README.md

Feign makes writing java http clients easier

Feign is a java to http client binder inspired by Dagger, Retrofit, JAXRS-2.0, and WebSocket. Feign's first goal was reducing the complexity of binding Denominator uniformly to http apis regardless of restfulness.

Disclaimer

Feign is experimental and being simplified further in version 5. Particularly, this will impact how encoders and encoders are declared, and remove support for observable methods.

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.

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

static class Contributor {
  String login;
  int contributions;
}

public static void main(String... args) {
  GitHub github = Feign.create(GitHub.class, "https://api.github.com", new GsonModule());

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

Feign includes a fully functional json codec in the feign-gson extension. See the Decoder section for how to write your own.

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:

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.

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

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.

CloudDNS cloudDNS = Feign.builder().target(new CloudIdentityTarget<CloudDNS>(user, apiKey));

You can find several 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 adds default encoders and decoders so you get get started with a JSON api.

Integration requires you pass new GsonModule() to Feign.create(), or add it to your graph with Dagger:

GitHub github = Feign.create(GitHub.class, "https://api.github.com", new GsonModule());

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:

api = Feign.builder()
           .decoder(SAXDecoder.builder()
                              .registerContentHandler(UserIdHandler.class)
                              .build())
           .target(Api.class, "https://apihost");

JAX-RS

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

Ribbon

RibbonModule overrides URL resolution of Feign's client, adding smart routing and resiliency capabilities provided by Ribbon.

Integration requires you to pass your ribbon client name as the host part of the url, for example myAppProd.

MyService api = Feign.create(MyService.class, "https://myAppProd", new RibbonModule());

Decoders

The last argument to Feign.create allows you to specify additional configuration such as how to decode a responses, modeled in Dagger.

If any methods in your interface return types besides Response, void or String, you'll need to configure a Decoder.

The GsonModule in the feign-gson extension configures a Decoder which parses objects from JSON using reflection.

Here's how you could write this yourself, using whatever library you prefer:

@Module(library = true)
static class JsonModule {
  @Provides Decoder decoder(final JsonParser parser) {
    return new Decoder() {

      @Override public Object decode(Response response, Type type) throws IOException {
        return parser.readJson(response.body().asReader(), type);
      }

    };
  }
}

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.

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

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:

@Module(overrides = true)
class Overrides {
  @Provides @Singleton Logger.Level provideLoggerLevel() {
    return Logger.Level.FULL;
  }

  @Provides @Singleton Logger provideLogger() {
    return new Logger.JavaLogger().appendToFile("logs/http.log");
  }
}
GitHub github = Feign.create(GitHub.class, "https://api.github.com", new GsonGitHubModule(), new Overrides());