You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
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:
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...)
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. ⏲
Metric API
Provide a first-class Metrics API that users can tap into to gain insight into the request/response lifecycle. Possibly provide better OpenTracing support.
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.
interfaceGitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") Stringowner, @Param("repo") Stringrepo);
@RequestLine("POST /repos/{owner}/{repo}/issues")
voidcreateIssue(Issueissue, @Param("owner") Stringowner, @Param("repo") Stringrepo);
}
publicstaticclassContributor {
Stringlogin;
intcontributions;
}
publicstaticclassIssue {
Stringtitle;
Stringbody;
List<String> assignees;
intmilestone;
List<String> labels;
}
publicclassMyApp {
publicstaticvoidmain(String... args) {
GitHubgithub = Feign.builder()
.decoder(newGsonDecoder())
.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 (Contributorcontributor : 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.
@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.
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
publicinterfaceGitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") Stringowner, @Param("repo") Stringrepository);
classContributor {
Stringlogin;
intcontributions;
}
}
publicclassMyApp {
publicstaticvoidmain(String[] args) {
GitHubgithub = Feign.builder()
.decoder(newGsonDecoder())
.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. Exampleowner 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.
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.
@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:
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.
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:
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 example:
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.
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:
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:
SOAP includes an encoder and decoder you can use with an XML API.
This module adds support for encoding and decoding SOAP Body objects via JAXB and SOAPMessage. It also provides SOAPFault decoding capabilities by wrapping them into the original javax.xml.ws.soap.SOAPFaultException, so that you'll only need to catch SOAPFaultException in order to handle SOAPFault.
Add SOAPEncoder and/or SOAPDecoder to your Feign.Builder like so:
NB: you may also need to add SOAPErrorDecoder if SOAP Faults are returned in response with error http codes (4xx, 5xx, ...)
SLF4J
SLF4JModule allows directing Feign's logging to SLF4J, allowing you to easily use a logging backend of your choice (Logback, Log4J, etc.)
To use SLF4J with Feign, add both the SLF4J module and an SLF4J binding of your choice to your classpath. Then, configure Feign to use the Slf4jLogger:
If you need to pre-process the response before give it to the Decoder, you can use the mapAndDecode builder method.
An example use case is dealing with an API that only serves jsonp, you will maybe need to unwrap the jsonp before
send it to the Json decoder of your choice:
The simplest way to send a request body to a server is to define a POST method that has a String or byte[] parameter without any annotations on it. You will likely need to add a Content-Type header.
In cases where both the header field keys and values are dynamic and the range of possible keys cannot
be known ahead of time and may vary between different method calls in the same api/client (e.g. custom
metadata header fields such as "x-amz-meta-*" or "x-goog-meta-*"), a Map parameter can be annotated
with HeaderMap to construct a query that uses the contents of the map as its header parameters.
These approaches specify header entries as part of the api and do not require any customizations
when building the Feign client.
Setting headers per target
To customize headers for each request method on a Target, a RequestInterceptor can be used. RequestInterceptors can be
shared across Target instances and are expected to be thread-safe. RequestInterceptors are applied to all request
methods on a Target.
If you need per method customization, a custom Target is required, as the a RequestInterceptor does not have access to
the current method metadata.
For an example of setting headers using a RequestInterceptor, see the Request Interceptors section.
These approaches depend on the custom RequestInterceptor or Target being set on the Feign
client when it is built and can be used as a way to set headers on all api calls on a per-client
basis. This can be useful for doing things such as setting an authentication token in the header
of all api requests on a per-client basis. The methods are run when the api call is made on the
thread that invokes the api call, which allows the headers to be set dynamically at call time and
in a context-specific manner -- for example, thread-local storage can be used to set different
header values depending on the invoking thread, which can be useful for things such as setting
thread-specific trace identifiers for requests.
Advanced usage
Base Apis
In many cases, apis for a service follow the same conventions. Feign supports this pattern via single-inheritance interfaces.
A Note on JavaLogger:
Avoid using of default JavaLogger() constructor - it was marked as deprecated and will be removed soon.
The SLF4JLogger (see above) may also be of interest.
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.
Parameters annotated with Param expand based on their toString. By
specifying a custom Param.Expander, users can control this behavior,
for example formatting dates.
When used in this manner, without specifying a custom QueryMapEncoder, the query map will be generated using member variable names as query parameter names. The following POJO will generate query params of "/find?name={name}&number={number}" (order of included query parameters not guaranteed, and as usual, if any value is null, it will be left out).
When annotating objects with @QueryMap, the default encoder uses reflection to inspect provided objects Fields to expand the objects values into a query string. If you prefer that the query string be built using getter and setter methods, as defined in the Java Beans API, please use the BeanQueryMapEncoder
All responses that result in an HTTP status not in the 2xx range will trigger the ErrorDecoder's decode method, allowing
you to handle the response, wrap the failure into a custom exception or perform any additional processing.
If you want to retry the request again, throw a RetryableException. This will invoke the registered
Retryer.
Retry
Feign, by default, will automatically retry IOExceptions, regardless of HTTP method, treating them as transient network
related exceptions, and any RetryableException thrown from an ErrorDecoder. To customize this
behavior, register a custom Retryer instance via the builder.
Retryers are responsible for determining if a retry should occur by returning either a true or
false from the method continueOrPropagate(RetryableException e); A Retryer instance will be
created for each Client execution, allowing you to maintain state bewteen each request if desired.
If the retry is determined to be unsuccessful, the last RetryException will be thrown. To throw the original
cause that led to the unsuccessful retry, build your Feign client with the exceptionPropagationPolicy() option.
Static and Default Methods
Interfaces targeted by Feign may have static or default methods (if using Java 8+).
These allows Feign clients to contain logic that is not expressly defined by the underlying API.
For example, static methods make it easy to specify common client build configurations; default methods can be used to compose queries or define default parameters.