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.
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.
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.
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
This is a map with current key features provided by feign:
Making API clients easier
Logger API refactorLogger 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 refactorRetry API to support user-supplied conditions and better control over back-off policies. This may result in non-backward-compatible breaking changesCompletableFutureFuture 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.java.util.concurrent.Flow.The feign library is available from Maven Central.
<dependency>
<groupId>io.github.openfeign</groupId>
<artifactId>feign-core</artifactId>
<version>??feign.version??</version>
</dependency>
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 + ")");
}
}
}
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.URIparameter and Feign will use that value as the request target.
java @RequestLine("POST /repos/{owner}/{repo}/issues") void createIssue(URI host, Issue issue, @Param("owner") String owner, @Param("repo") String repo);
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]*}
RequestLine and QueryMap templates follow the URI Template - RFC 6570 specification for Level 1 templates, which specifies the following:
encoded via a @Param annotation.We also have limited support for Level 3, Path Style Expressions, with the following restrictions:
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 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 thedecodeSlashproperty on the@RequestLinetofalse.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%2Bwhen found on a query string.If you wish to use
+as a space, then use the literalcharacter or encode the value directly as%20
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.
Headers and HeaderMap templates follow the same rules as Request Parameter Expansion
with the following alterations:
See Headers for examples.
A Note on
@Paramparameters and their names:All expressions with the same name, regardless of their position on the
@RequestLine,@QueryMap,@BodyTemplate, or@Headerswill resolve to the same value. In the following example, the value ofcontentType, will be used to resolve both the header and path expression:
java 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.
Body templates follow the same rules as Request Parameter Expansion
with the fo
$ claude mcp add feign \
-- python -m otcore.mcp_server <graph>