[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"project-4119":3},{"id":4,"name":5,"fullName":6,"owner":7,"repo":5,"description":8,"homepage":9,"htmlUrl":10,"language":11,"languages":10,"totalLinesOfCode":10,"stars":12,"forks":13,"watchers":14,"openIssues":15,"contributorsCount":16,"subscribersCount":16,"size":16,"stars1d":16,"stars7d":17,"stars30d":18,"stars90d":16,"forks30d":16,"starsTrendScore":17,"compositeScore":19,"rankGlobal":10,"rankLanguage":10,"license":20,"archived":21,"fork":21,"defaultBranch":22,"hasWiki":23,"hasPages":21,"topics":24,"createdAt":10,"pushedAt":10,"updatedAt":31,"readmeContent":32,"aiSummary":33,"trendingCount":16,"starSnapshotCount":16,"syncStatus":17,"lastSyncTime":34,"discoverSource":35},4119,"feign","OpenFeign\u002Ffeign","OpenFeign","Feign makes writing java http clients easier","",null,"Java",9782,1937,393,221,0,2,5,40.86,"Apache License 2.0",false,"master",true,[25,26,27,28,29,30],"http-client","interface","java","jax-rs","okhttp3","slf4j","2026-06-12 02:00:58","# Feign simplifies the process of writing Java HTTP clients\n\n[![Join the chat at https:\u002F\u002Fgitter.im\u002FOpenFeign\u002Ffeign](https:\u002F\u002Fbadges.gitter.im\u002FJoin%20Chat.svg)](https:\u002F\u002Fgitter.im\u002FOpenFeign\u002Ffeign?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge)\n[![CI](https:\u002F\u002Fgithub.com\u002FOpenFeign\u002Ffeign\u002Factions\u002Fworkflows\u002Fbuild.yml\u002Fbadge.svg?branch=master)](https:\u002F\u002Fgithub.com\u002FOpenFeign\u002Ffeign\u002Factions\u002Fworkflows\u002Fbuild.yml)\n[![Maven Central](https:\u002F\u002Fmaven-badges.herokuapp.com\u002Fmaven-central\u002Fio.github.openfeign\u002Ffeign-core\u002Fbadge.png)](https:\u002F\u002Fsearch.maven.org\u002Fartifact\u002Fio.github.openfeign\u002Ffeign-core\u002F)\n\nFeign is a Java to HTTP client binder inspired by [Retrofit](https:\u002F\u002Fgithub.com\u002Fsquare\u002Fretrofit), [JAXRS-2.0](https:\u002F\u002Fjax-rs-spec.java.net\u002Fnonav\u002F2.0\u002Fapidocs\u002Findex.html), and [WebSocket](http:\u002F\u002Fwww.oracle.com\u002Ftechnetwork\u002Farticles\u002Fjava\u002Fjsr356-1937161.html).  Feign's first goal was reducing the complexity of binding [Denominator](https:\u002F\u002Fgithub.com\u002FNetflix\u002FDenominator) uniformly to HTTP APIs regardless of [ReSTfulness](http:\u002F\u002Fwww.slideshare.net\u002Fadrianfcole\u002F99problems).\n\n---\n### Why Feign and not X?\n\nFeign 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.\n\n### How does Feign work?\n\nFeign 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.\n\n### Java Version Compatibility\n\nFeign 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\n\n## Feature overview\n\nThis is a map with current key features provided by feign:\n\n![MindMap overview](http:\u002F\u002Fwww.plantuml.com\u002Fplantuml\u002Fproxy?cache=no&src=https:\u002F\u002Fraw.githubusercontent.com\u002FOpenFeign\u002Ffeign\u002Fmaster\u002Fsrc\u002Fdocs\u002Foverview-mindmap.iuml)\n\n# Roadmap\n## Feign 11 and beyond\nMaking _API_ clients easier\n\nShort Term - What we're working on now. ⏰\n---\n* Response Caching\n  * 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.\n  * Support in-memory caching and external cache implementations (EhCache, Google, Spring, etc...)\n* Complete URI Template expression support\n  * Support [level 1 through level 4](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc6570#section-1.2) URI template expressions.\n  * Use [URI Templates TCK](https:\u002F\u002Fgithub.com\u002Furi-templates\u002Furitemplate-test) to verify compliance.\n* `Logger` API refactor\n  * 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.\n* `Retry` API refactor\n  * 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**\n\nMedium Term - What's up next. ⏲\n---\n* Async execution support via `CompletableFuture`\n  * Allow for `Future` chaining and executor management for the request\u002Fresponse lifecycle.  **Implementation will require non-backward-compatible breaking changes**.  However this feature is required before Reactive execution can be considered.\n* Reactive execution support via [Reactive Streams](https:\u002F\u002Fwww.reactive-streams.org\u002F)\n  * For JDK 9+, consider a native implementation that uses `java.util.concurrent.Flow`.\n  * Support for [Project Reactor](https:\u002F\u002Fprojectreactor.io\u002F) and [RxJava 2+](https:\u002F\u002Fgithub.com\u002FReactiveX\u002FRxJava) implementations on JDK 8.\n\nLong Term - The future ☁️\n---\n* Additional Circuit Breaker Support.\n  * Support additional Circuit Breaker implementations like [Resilience4J](https:\u002F\u002Fresilience4j.readme.io\u002F) and Spring Circuit Breaker\n\n---\n\n# Usage\n\nThe feign library is available from [Maven Central](https:\u002F\u002Fcentral.sonatype.com\u002Fartifact\u002Fio.github.openfeign\u002Ffeign-core).\n\n```xml\n\u003Cdependency>\n    \u003CgroupId>io.github.openfeign\u003C\u002FgroupId>\n    \u003CartifactId>feign-core\u003C\u002FartifactId>\n    \u003Cversion>??feign.version??\u003C\u002Fversion>\n\u003C\u002Fdependency>\n```\n\n### Basics\n\nUsage typically looks like this, an adaptation of the [canonical Retrofit sample](https:\u002F\u002Fgithub.com\u002Fsquare\u002Fretrofit\u002Fblob\u002Fmaster\u002Fsamples\u002Fsrc\u002Fmain\u002Fjava\u002Fcom\u002Fexample\u002Fretrofit\u002FSimpleService.java).\n\n```java\ninterface GitHub {\n  @RequestLine(\"GET \u002Frepos\u002F{owner}\u002F{repo}\u002Fcontributors\")\n  List\u003CContributor> contributors(@Param(\"owner\") String owner, @Param(\"repo\") String repo);\n\n  @RequestLine(\"POST \u002Frepos\u002F{owner}\u002F{repo}\u002Fissues\")\n  void createIssue(Issue issue, @Param(\"owner\") String owner, @Param(\"repo\") String repo);\n\n}\n\npublic static class Contributor {\n  String login;\n  int contributions;\n}\n\npublic static class Issue {\n  String title;\n  String body;\n  List\u003CString> assignees;\n  int milestone;\n  List\u003CString> labels;\n}\n\npublic class MyApp {\n  public static void main(String... args) {\n    GitHub github = Feign.builder()\n                         .decoder(new GsonDecoder())\n                         .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n\n    \u002F\u002F Fetch and print a list of the contributors to this library.\n    List\u003CContributor> contributors = github.contributors(\"OpenFeign\", \"feign\");\n    for (Contributor contributor : contributors) {\n      System.out.println(contributor.login + \" (\" + contributor.contributions + \")\");\n    }\n  }\n}\n```\n\n### Interface Annotations\n\nFeign annotations define the `Contract` between the interface and how the underlying client\nshould work.  Feign's default contract defines the following annotations:\n\n| Annotation     | Interface Target | Usage |\n|----------------|------------------|-------|\n| `@RequestLine` | Method           | Defines the `HttpMethod` and `UriTemplate` for request.  `Expressions`, values wrapped in curly-braces `{expression}` are resolved using their corresponding `@Param` annotated parameters. |\n| `@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). |\n| `@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. |\n| `@QueryMap`    | Parameter        | Defines a `Map` of name-value pairs, or POJO, to expand into a query string. |\n| `@HeaderMap`   | Parameter        | Defines a `Map` of name-value pairs, to expand into `Http Headers` |\n| `@Body`        | Method           | Defines a `Template`, similar to a `UriTemplate` and `HeaderTemplate`, that uses `@Param` annotated values to resolve the corresponding `Expressions`.|\n\n\n> **Overriding the Request Line**\n>\n> If there is a need to target a request to a different host then the one supplied when the Feign client was created, or\n> you want to supply a target host for each request, include a `java.net.URI` parameter and Feign will use that value\n> as the request target.\n>\n> ```java\n> @RequestLine(\"POST \u002Frepos\u002F{owner}\u002F{repo}\u002Fissues\")\n> void createIssue(URI host, Issue issue, @Param(\"owner\") String owner, @Param(\"repo\") String repo);\n> ```\n>\n\n### Templates and Expressions\n\nFeign `Expressions` represent Simple String Expressions (Level 1) as defined by [URI Template - RFC 6570](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc6570).  `Expressions` are expanded using\ntheir corresponding `Param` annotated method parameters.\n\n*Example*\n\n```java\npublic interface GitHub {\n\n  @RequestLine(\"GET \u002Frepos\u002F{owner}\u002F{repo}\u002Fcontributors\")\n  List\u003CContributor> contributors(@Param(\"owner\") String owner, @Param(\"repo\") String repository);\n\n  class Contributor {\n    String login;\n    int contributions;\n  }\n}\n\npublic class MyApp {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n                         .decoder(new GsonDecoder())\n                         .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n\n    \u002F* The owner and repository parameters will be used to expand the owner and repo expressions\n     * defined in the RequestLine.\n     *\n     * the resulting uri will be https:\u002F\u002Fapi.github.com\u002Frepos\u002FOpenFeign\u002Ffeign\u002Fcontributors\n     *\u002F\n    github.contributors(\"OpenFeign\", \"feign\");\n  }\n}\n```\n\nExpressions must be enclosed in curly braces `{}` and may contain regular expression patterns, separated by a colon `:`  to restrict\nresolved values.  *Example* `owner` must be alphabetic. `{owner:[a-zA-Z]*}`\n\n#### Request Parameter Expansion\n\n`RequestLine` and `QueryMap` templates follow the [URI Template - RFC 6570](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc6570) specification for Level 1 templates, which specifies the following:\n\n* Unresolved expressions are omitted.\n* All literals and variable values are pct-encoded, if not already encoded or marked `encoded` via a `@Param` annotation.\n\nWe also have limited support for Level 3, Path Style Expressions, with the following restrictions:\n\n* Maps and Lists are expanded by default.\n* Only Single variable templates are supported.\n\n*Examples:*\n\n```\n{;who}             ;who=fred\n{;half}            ;half=50%25\n{;empty}           ;empty\n{;list}            ;list=red;list=green;list=blue\n{;map}             ;semi=%3B;dot=.;comma=%2C\n```\n\n```java\npublic interface MatrixService {\n\n  @RequestLine(\"GET \u002Frepos{;owners}\")\n  List\u003CContributor> contributors(@Param(\"owners\") List\u003CString> owners);\n\n  class Contributor {\n    String login;\n    int contributions;\n  }\n}\n```\n\nIf `owners` in the above example is defined as `Matt, Jeff, Susan`, the uri will expand to `\u002Frepos;owners=Matt;owners=Jeff;owners=Susan` \n\nFor more information see [RFC 6570, Section 3.2.7](https:\u002F\u002Fdatatracker.ietf.org\u002Fdoc\u002Fhtml\u002Frfc6570#section-3.2.7)\n\n#### Undefined vs. Empty Values ####\n\nUndefined expressions are expressions where the value for the expression is an explicit `null` or no value is provided.\nPer [URI Template - RFC 6570](https:\u002F\u002Ftools.ietf.org\u002Fhtml\u002Frfc6570), it is possible to provide an empty value\nfor an expression.  When Feign resolves an expression, it first determines if the value is defined, if it is then\nthe query parameter will remain.  If the expression is undefined, the query parameter is removed.  See below\nfor a complete breakdown.\n\n*Empty String*\n```java\npublic void test() {\n   Map\u003CString, Object> parameters = new LinkedHashMap\u003C>();\n   parameters.put(\"param\", \"\");\n   this.demoClient.test(parameters);\n}\n```\nResult\n```\nhttp:\u002F\u002Flocalhost:8080\u002Ftest?param=\n```\n\n*Missing*\n```java\npublic void test() {\n   Map\u003CString, Object> parameters = new LinkedHashMap\u003C>();\n   this.demoClient.test(parameters);\n}\n```\nResult\n```\nhttp:\u002F\u002Flocalhost:8080\u002Ftest\n```\n\n*Undefined*\n```java\npublic void test() {\n   Map\u003CString, Object> parameters = new LinkedHashMap\u003C>();\n   parameters.put(\"param\", null);\n   this.demoClient.test(parameters);\n}\n```\nResult\n```\nhttp:\u002F\u002Flocalhost:8080\u002Ftest\n```\n\nSee [Advanced Usage](#advanced-usage) for more examples.\n\n> **What about slashes? `\u002F`**\n>\n> @RequestLine templates do not encode slash `\u002F` characters by default.  To change this behavior, set the `decodeSlash` property on the `@RequestLine` to `false`.\n\n> **What about plus? `+`**\n>\n> Per the URI specification, a `+` sign is allowed in both the path and query segments of a URI, however, handling of\n> 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\n> `+` symbol should not represent a space and is explicitly encoded as `%2B` when found on a query string.\n>\n> If you wish to use `+` as a space, then use the literal ` ` character or encode the value directly as `%20`\n\n##### Custom Expansion\n\nThe `@Param` annotation has an optional property `expander` allowing for complete control over the individual parameter's expansion.\nThe `expander` property must reference a class that implements the `Expander` interface:\n\n```java\npublic interface Expander {\n    String expand(Object value);\n}\n```\nThe result of this method adheres to the same rules stated above.  If the result is `null` or an empty string,\nthe value is omitted.  If the value is not pct-encoded, it will be.  See [Custom @Param Expansion](#custom-param-expansion) for more examples.\n\n#### Request Headers Expansion\n\n`Headers` and `HeaderMap` templates follow the same rules as [Request Parameter Expansion](#request-parameter-expansion)\nwith the following alterations:\n\n* Unresolved expressions are omitted.  If the result is an empty header value, the entire header is removed.\n* No pct-encoding is performed.\n\nSee [Headers](#headers) for examples.\n\n> **A Note on `@Param` parameters and their names**:\n>\n> All expressions with the same name, regardless of their position on the `@RequestLine`, `@QueryMap`, `@BodyTemplate`, or `@Headers` will resolve to the same value.\n> In the following example, the value of `contentType`, will be used to resolve both the header and path expression:\n>\n> ```java\n> public interface ContentService {\n>   @RequestLine(\"GET \u002Fapi\u002Fdocuments\u002F{contentType}\")\n>   @Headers(\"Accept: {contentType}\")\n>   String getDocumentByType(@Param(\"contentType\") String type);\n> }\n>```\n>\n> Keep this in mind when designing your interfaces.\n\n#### Request Body Expansion\n\n`Body` templates follow the same rules as [Request Parameter Expansion](#request-parameter-expansion)\nwith the following alterations:\n\n* Unresolved expressions are omitted.\n* Expanded value will **not** be passed through an `Encoder` before being placed on the request body.\n* A `Content-Type` header must be specified.  See [Body Templates](#body-templates) for examples.\n\n---\n### Customization\n\nFeign has several aspects that can be customized.  \nFor simple cases, you can use `Feign.builder()` to construct an API interface with your custom components.\u003Cbr>\nFor request setting, you can use `options(Request.Options options)` on `target()` to set connectTimeout, connectTimeoutUnit, readTimeout, readTimeoutUnit, followRedirects.\u003Cbr>\nFor example:\n\n```java\ninterface Bank {\n  @RequestLine(\"POST \u002Faccount\u002F{id}\")\n  Account getAccountInfo(@Param(\"id\") String id);\n}\n\npublic class BankService {\n  public static void main(String[] args) {\n    Bank bank = Feign.builder()\n        .decoder(new AccountDecoder())\n        .options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true))\n        .target(Bank.class, \"https:\u002F\u002Fapi.examplebank.com\");\n  }\n}\n```\n\n### Multiple Interfaces\nFeign can produce multiple api interfaces.  These are defined as `Target\u003CT>` (default `HardCodedTarget\u003CT>`), which allow for dynamic discovery and decoration of requests prior to execution.\n\nFor example, the following pattern might decorate each request with the current url and auth token from the identity service.\n\n```java\npublic class CloudService {\n  public static void main(String[] args) {\n    CloudDNS cloudDNS = Feign.builder()\n      .target(new CloudIdentityTarget\u003CCloudDNS>(user, apiKey));\n  }\n\n  class CloudIdentityTarget extends Target\u003CCloudDNS> {\n    \u002F* implementation of a Target *\u002F\n  }\n}\n```\n\n### Examples\nFeign includes example [GitHub](.\u002Fexample-github) and [Wikipedia](.\u002Fexample-wikipedia) clients. The denominator project can also be scraped for Feign in practice. Particularly, look at its [example daemon](https:\u002F\u002Fgithub.com\u002FNetflix\u002Fdenominator\u002Ftree\u002Fmaster\u002Fexample-daemon).\n\n---\n### Integrations\nFeign intends to work well with other Open Source tools.  Modules are welcome to integrate with your favorite projects!\n\n### Encoder\u002FDecoder\n\n#### Gson\n[Gson](.\u002Fgson) includes a codec you can use with a JSON API.\n\n```java\nGitHub github = Feign.builder()\n                     .codec(new GsonCodec())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n```\n\nYou can also configure the encoder and decoder separately:\n\n```java\nGitHub github = Feign.builder()\n                     .encoder(new GsonEncoder())\n                     .decoder(new GsonDecoder())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n```\n\n#### Jackson\n[Jackson](.\u002Fjackson) includes a codec you can use with a JSON API.\n\n```java\nGitHub github = Feign.builder()\n                     .codec(new JacksonCodec())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n```\n\nYou can also configure the encoder and decoder separately:\n\n```java\nGitHub github = Feign.builder()\n                     .encoder(new JacksonEncoder())\n                     .decoder(new JacksonDecoder())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n```\n\nFor the lighter weight Jackson Jr, use `JacksonJrEncoder` and `JacksonJrDecoder` from\nthe [Jackson Jr Module](.\u002Fjackson-jr).\n\n#### Moshi\n[Moshi](.\u002Fmoshi) includes a codec you can use with a JSON API.\n\n```java\nGitHub github = Feign.builder()\n                     .codec(new MoshiCodec())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n```\n\nYou can also configure the encoder and decoder separately:\n\n```java\nGitHub github = Feign.builder()\n                     .encoder(new MoshiEncoder())\n                     .decoder(new MoshiDecoder())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n```\n\n#### Sax\n[SaxDecoder](.\u002Fsax) allows you to decode XML in a way that is compatible with normal JVM and also Android environments.\n\nHere's an example of how to configure Sax response parsing:\n```java\nApi api = Feign.builder()\n               .decoder(SAXDecoder.builder()\n                                  .registerContentHandler(UserIdHandler.class)\n                                  .build())\n               .target(Api.class, \"https:\u002F\u002Fapihost\");\n```\n\n#### JAXB\n[JAXB](.\u002Fjaxb) includes a codec you can use with an XML API.\n\n```java\nApi api = Feign.builder()\n               .codec(new JAXBCodec(jaxbFactory))\n               .target(Api.class, \"https:\u002F\u002Fapihost\");\n```\n\nYou can also configure the encoder and decoder separately:\n\n```java\nApi api = Feign.builder()\n               .encoder(new JAXBEncoder(jaxbFactory))\n               .decoder(new JAXBDecoder(jaxbFactory))\n               .target(Api.class, \"https:\u002F\u002Fapihost\");\n```\n\n#### SOAP\n[SOAP](.\u002Fsoap) includes a codec you can use with an XML API.\n\nThis 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.\n\n```java\nApi api = Feign.builder()\n               .codec(new SOAPCodec(jaxbFactory))\n               .errorDecoder(new SOAPErrorDecoder())\n               .target(MyApi.class, \"http:\u002F\u002Fapi\");\n```\n\nYou can also configure the encoder and decoder separately:\n\n```java\nApi api = Feign.builder()\n               .encoder(new SOAPEncoder(jaxbFactory))\n               .decoder(new SOAPDecoder(jaxbFactory))\n               .errorDecoder(new SOAPErrorDecoder())\n               .target(MyApi.class, \"http:\u002F\u002Fapi\");\n```\n\nNB: you may also need to add `SOAPErrorDecoder` if SOAP Faults are returned in response with error http codes (4xx, 5xx, ...)\n\n#### Fastjson2\n\n[fastjson2](.\u002Ffastjson2) includes a codec you can use with a JSON API.\n\n```java\nGitHub github = Feign.builder()\n                     .codec(new Fastjson2Codec())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n```\n\nYou can also configure the encoder and decoder separately:\n\n```java\nGitHub github = Feign.builder()\n                     .encoder(new Fastjson2Encoder())\n                     .decoder(new Fastjson2Decoder())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n```\n\n### Contract\n\n#### JAX-RS\n[JAXRSContract](.\u002Fjaxrs) overrides annotation processing to instead use standard ones supplied by the JAX-RS specification.  This is currently targeted at the 1.1 spec.\n\nHere's the example above re-written to use JAX-RS:\n```java\ninterface GitHub {\n  @GET @Path(\"\u002Frepos\u002F{owner}\u002F{repo}\u002Fcontributors\")\n  List\u003CContributor> contributors(@PathParam(\"owner\") String owner, @PathParam(\"repo\") String repo);\n}\n\npublic class Example {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n                       .contract(new JAXRSContract())\n                       .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n  }\n}\n```\n\n### Client\n\n#### OkHttp\n[OkHttpClient](.\u002Fokhttp) directs Feign's http requests to [OkHttp](http:\u002F\u002Fsquare.github.io\u002Fokhttp\u002F), which enables SPDY and better network control.\n\nTo use OkHttp with Feign, add the OkHttp module to your classpath. Then, configure Feign to use the OkHttpClient:\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n                     .client(new OkHttpClient())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n  }\n}\n```\n\n#### Ribbon\n[RibbonClient](.\u002Fribbon) overrides URL resolution of Feign's client, adding smart routing and resiliency capabilities provided by [Ribbon](https:\u002F\u002Fgithub.com\u002FNetflix\u002Fribbon).\n\nIntegration requires you to pass your ribbon client name as the host part of the url, for example `myAppProd`.\n```java\npublic class Example {\n  public static void main(String[] args) {\n    MyService api = Feign.builder()\n          .client(RibbonClient.create())\n          .target(MyService.class, \"https:\u002F\u002FmyAppProd\");\n  }\n}\n```\n\n#### Java 11 Http2\n[Http2Client](.\u002Fjava11) directs Feign's http requests to Java11 [New HTTP\u002F2 Client](https:\u002F\u002Fopenjdk.java.net\u002Fjeps\u002F321) that implements HTTP\u002F2.\n\nTo use New HTTP\u002F2 Client with Feign, use Java SDK 11. Then, configure Feign to use the Http2Client:\n\n```java\nGitHub github = Feign.builder()\n                     .client(new Http2Client())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n```\n\n### Breaker\n\n#### Hystrix\n[HystrixFeign](.\u002Fhystrix) configures circuit breaker support provided by [Hystrix](https:\u002F\u002Fgithub.com\u002FNetflix\u002FHystrix).\n\nTo use Hystrix with Feign, add the Hystrix module to your classpath. Then use the `HystrixFeign` builder:\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    MyService api = HystrixFeign.builder().target(MyService.class, \"https:\u002F\u002FmyAppProd\");\n  }\n}\n```\n\n### Logger\n\n#### SLF4J\n[SLF4JModule](.\u002Fslf4j) allows directing Feign's logging to [SLF4J](http:\u002F\u002Fwww.slf4j.org\u002F), allowing you to easily use a logging backend of your choice (Logback, Log4J, etc.)\n\nTo 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:\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n                     .logger(new Slf4jLogger())\n                     .logLevel(Level.FULL)\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n  }\n}\n```\n\n### Decoders\n`Feign.builder()` allows you to specify additional configuration such as how to decode a response.\n\nIf any methods in your interface return types besides `Response`, `String`, `byte[]` or `void`, you'll need to configure a non-default `Decoder`.\n\nHere's how to configure JSON decoding (using the `feign-gson` extension):\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n                     .decoder(new GsonDecoder())\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n  }\n}\n```\n\nIf you need to pre-process the response before give it to the Decoder, you can use the `mapAndDecode` builder method.\nAn example use case is dealing with an API that only serves jsonp, you will maybe need to unwrap the jsonp before\nsend it to the Json decoder of your choice:\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    JsonpApi jsonpApi = Feign.builder()\n                         .mapAndDecode((response, type) -> jsopUnwrap(response, type), new GsonDecoder())\n                         .target(JsonpApi.class, \"https:\u002F\u002Fsome-jsonp-api.com\");\n  }\n}\n```\n\nIf any methods in your interface return type `Stream`, you'll need to configure a `StreamDecoder`.\n\nHere's how to configure Stream decoder without delegate decoder:\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n            .decoder(StreamDecoder.create((r, t) -> {\n              BufferedReader bufferedReader = new BufferedReader(r.body().asReader(UTF_8));\n              return bufferedReader.lines().iterator();\n            }))\n            .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n  }\n}\n``` \n\nHere's how to configure Stream decoder with delegate decoder:\n\n```java\n\npublic class Example {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n            .decoder(StreamDecoder.create((r, t) -> {\n              BufferedReader bufferedReader = new BufferedReader(r.body().asReader(UTF_8));\n              return bufferedReader.lines().iterator();\n            }, (r, t) -> \"this is delegate decoder\"))\n            .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n  }\n}\n```\n\n### Encoders\nThe 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.\n\n```java\ninterface LoginClient {\n  @RequestLine(\"POST \u002F\")\n  @Headers(\"Content-Type: application\u002Fjson\")\n  void login(String content);\n}\n\npublic class Example {\n  public static void main(String[] args) {\n    client.login(\"{\\\"user_name\\\": \\\"denominator\\\", \\\"password\\\": \\\"secret\\\"}\");\n  }\n}\n```\n\nBy configuring an `Encoder`, you can send a type-safe request body. Here's an example using the `feign-gson` extension:\n\n```java\nstatic class Credentials {\n  final String user_name;\n  final String password;\n\n  Credentials(String user_name, String password) {\n    this.user_name = user_name;\n    this.password = password;\n  }\n}\n\ninterface LoginClient {\n  @RequestLine(\"POST \u002F\")\n  void login(Credentials creds);\n}\n\npublic class Example {\n  public static void main(String[] args) {\n    LoginClient client = Feign.builder()\n                              .encoder(new GsonEncoder())\n                              .target(LoginClient.class, \"https:\u002F\u002Ffoo.com\");\n\n    client.login(new Credentials(\"denominator\", \"secret\"));\n  }\n}\n```\n\n### @Body templates\nThe `@Body` annotation indicates a template to expand using parameters annotated with `@Param`. You will likely need to add a `Content-Type` header.\n\n```java\ninterface LoginClient {\n\n  @RequestLine(\"POST \u002F\")\n  @Headers(\"Content-Type: application\u002Fxml\")\n  @Body(\"\u003Clogin \\\"user_name\\\"=\\\"{user_name}\\\" \\\"password\\\"=\\\"{password}\\\"\u002F>\")\n  void xml(@Param(\"user_name\") String user, @Param(\"password\") String password);\n\n  @RequestLine(\"POST \u002F\")\n  @Headers(\"Content-Type: application\u002Fjson\")\n  \u002F\u002F json curly braces must be escaped!\n  @Body(\"%7B\\\"user_name\\\": \\\"{user_name}\\\", \\\"password\\\": \\\"{password}\\\"%7D\")\n  void json(@Param(\"user_name\") String user, @Param(\"password\") String password);\n}\n\npublic class Example {\n  public static void main(String[] args) {\n    client.xml(\"denominator\", \"secret\"); \u002F\u002F \u003Clogin \"user_name\"=\"denominator\" \"password\"=\"secret\"\u002F>\n    client.json(\"denominator\", \"secret\"); \u002F\u002F {\"user_name\": \"denominator\", \"password\": \"secret\"}\n  }\n}\n```\n\n### Headers\nFeign supports settings headers on requests either as part of the api or as part of the client\ndepending on the use case.\n\n#### Set headers using apis\nIn cases where specific interfaces or calls should always have certain header values set, it\nmakes sense to define headers as part of the api.\n\nStatic headers can be set on an api interface or method using the `@Headers` annotation.\n\n```java\n@Headers(\"Accept: application\u002Fjson\")\ninterface BaseApi\u003CV> {\n  @Headers(\"Content-Type: application\u002Fjson\")\n  @RequestLine(\"PUT \u002Fapi\u002F{key}\")\n  void put(@Param(\"key\") String key, V value);\n}\n```\n\nMethods can specify dynamic content for static headers using variable expansion in `@Headers`.\n\n```java\npublic interface Api {\n   @RequestLine(\"POST \u002F\")\n   @Headers(\"X-Ping: {token}\")\n   void post(@Param(\"token\") String token);\n}\n```\n\nIn cases where both the header field keys and values are dynamic and the range of possible keys cannot\nbe known ahead of time and may vary between different method calls in the same api\u002Fclient (e.g. custom\nmetadata header fields such as \"x-amz-meta-\\*\" or \"x-goog-meta-\\*\"), a Map parameter can be annotated\nwith `HeaderMap` to construct a query that uses the contents of the map as its header parameters.\n\n```java\npublic interface Api {\n   @RequestLine(\"POST \u002F\")\n   void post(@HeaderMap Map\u003CString, Object> headerMap);\n}\n```\n\nThese approaches specify header entries as part of the api and do not require any customizations\nwhen building the Feign client.\n\n#### Setting headers per target\nTo customize headers for each request method on a Target, a RequestInterceptor can be used. RequestInterceptors can be\nshared across Target instances and are expected to be thread-safe. RequestInterceptors are applied to all request\nmethods on a Target.\n\nIf you need per method customization, a custom Target is required, as the a RequestInterceptor does not have access to\nthe current method metadata.\n\nFor an example of setting headers using a `RequestInterceptor`, see the `Request Interceptors` section.\n\nHeaders can be set as part of a custom `Target`.\n\n```java\n  static class DynamicAuthTokenTarget\u003CT> implements Target\u003CT> {\n    public DynamicAuthTokenTarget(Class\u003CT> clazz,\n                                  UrlAndTokenProvider provider,\n                                  ThreadLocal\u003CString> requestIdProvider);\n\n    @Override\n    public Request apply(RequestTemplate input) {\n      TokenIdAndPublicURL urlAndToken = provider.get();\n      if (input.url().indexOf(\"http\") != 0) {\n        input.insert(0, urlAndToken.publicURL);\n      }\n      input.header(\"X-Auth-Token\", urlAndToken.tokenId);\n      input.header(\"X-Request-ID\", requestIdProvider.get());\n\n      return input.request();\n    }\n  }\n\n  public class Example {\n    public static void main(String[] args) {\n      Bank bank = Feign.builder()\n              .target(new DynamicAuthTokenTarget(Bank.class, provider, requestIdProvider));\n    }\n  }\n```\n\nThese approaches depend on the custom `RequestInterceptor` or `Target` being set on the Feign\nclient when it is built and can be used as a way to set headers on all api calls on a per-client\nbasis. This can be useful for doing things such as setting an authentication token in the header\nof all api requests on a per-client basis. The methods are run when the api call is made on the\nthread that invokes the api call, which allows the headers to be set dynamically at call time and\nin a context-specific manner -- for example, thread-local storage can be used to set different\nheader values depending on the invoking thread, which can be useful for things such as setting\nthread-specific trace identifiers for requests.\n\n#### Set zero Content-Length Header\n\nTo specify `Content-Length: 0` header when making a request with empty body, system property `sun.net.http.allowRestrictedHeaders` should be set to `true`\n\nIf not, the `Content-Length` header will not be added.\n\n### Advanced usage\n\n#### Base Apis\nIn many cases, apis for a service follow the same conventions. Feign supports this pattern via single-inheritance interfaces.\n\nConsider the example:\n```java\ninterface BaseAPI {\n  @RequestLine(\"GET \u002Fhealth\")\n  String health();\n\n  @RequestLine(\"GET \u002Fall\")\n  List\u003CEntity> all();\n}\n```\n\nYou can define and target a specific api, inheriting the base methods.\n```java\ninterface CustomAPI extends BaseAPI {\n  @RequestLine(\"GET \u002Fcustom\")\n  String custom();\n}\n```\n\nIn many cases, resource representations are also consistent. For this reason, type parameters are supported on the base api interface.\n\n```java\n@Headers(\"Accept: application\u002Fjson\")\ninterface BaseApi\u003CV> {\n\n  @RequestLine(\"GET \u002Fapi\u002F{key}\")\n  V get(@Param(\"key\") String key);\n\n  @RequestLine(\"GET \u002Fapi\")\n  List\u003CV> list();\n\n  @Headers(\"Content-Type: application\u002Fjson\")\n  @RequestLine(\"PUT \u002Fapi\u002F{key}\")\n  void put(@Param(\"key\") String key, V value);\n}\n\ninterface FooApi extends BaseApi\u003CFoo> { }\n\ninterface BarApi extends BaseApi\u003CBar> { }\n```\n\n#### Logging\nYou can log the http messages going to and from the target by setting up a `Logger`.  Here's the easiest way to do that:\n```java\npublic class Example {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n                     .decoder(new GsonDecoder())\n                     .logger(new Logger.JavaLogger(\"GitHub.Logger\").appendToFile(\"logs\u002Fhttp.log\"))\n                     .logLevel(Logger.Level.FULL)\n                     .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n  }\n}\n```\n\n> **A Note on JavaLogger**:\n> Avoid using of default ```JavaLogger()``` constructor - it was marked as deprecated and will be removed soon.\n\nThe SLF4JLogger (see above) may also be of interest.\n\nTo filter out sensitive information like authorization or tokens\noverride methods `shouldLogRequestHeader` or `shouldLogResponseHeader`.\n\n#### Request Interceptors\nWhen you need to change all requests, regardless of their target, you'll want to configure a `RequestInterceptor`.\nFor example, if you are acting as an intermediary, you might want to propagate the `X-Forwarded-For` header.\n\n```java\nstatic class ForwardedForInterceptor implements RequestInterceptor {\n  @Override public void apply(RequestTemplate template) {\n    template.header(\"X-Forwarded-For\", \"origin.host.com\");\n  }\n}\n\npublic class Example {\n  public static void main(String[] args) {\n    Bank bank = Feign.builder()\n                 .decoder(accountDecoder)\n                 .requestInterceptor(new ForwardedForInterceptor())\n                 .target(Bank.class, \"https:\u002F\u002Fapi.examplebank.com\");\n  }\n}\n```\n\nAnother common example of an interceptor would be authentication, such as using the built-in `BasicAuthRequestInterceptor`.\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    Bank bank = Feign.builder()\n                 .decoder(accountDecoder)\n                 .requestInterceptor(new BasicAuthRequestInterceptor(username, password))\n                 .target(Bank.class, \"https:\u002F\u002Fapi.examplebank.com\");\n  }\n}\n```\n\n#### Custom @Param Expansion\nParameters annotated with `Param` expand based on their `toString`. By\nspecifying a custom `Param.Expander`, users can control this behavior,\nfor example formatting dates.\n\n```java\npublic interface Api {\n  @RequestLine(\"GET \u002F?since={date}\") Result list(@Param(value = \"date\", expander = DateToMillis.class) Date date);\n}\n```\n\n#### Dynamic Query Parameters\nA Map parameter can be annotated with `QueryMap` to construct a query that uses the contents of the map as its query parameters.\n\n```java\npublic interface Api {\n  @RequestLine(\"GET \u002Ffind\")\n  V find(@QueryMap Map\u003CString, Object> queryMap);\n}\n```\n\nThis may also be used to generate the query parameters from a POJO object using a `QueryMapEncoder`.\n\n```java\npublic interface Api {\n  @RequestLine(\"GET \u002Ffind\")\n  V find(@QueryMap CustomPojo customPojo);\n}\n```\n\nWhen used in this manner, without specifying a custom `QueryMapEncoder`, the query map will be generated using member variable names as query parameter names. You can annotate a specific field of `CustomPojo` with the `@Param` annotation to specify a different name to the query parameter. The following POJO will generate query params of \"\u002Ffind?name={name}&number={number}&region_id={regionId}\" (order of included query parameters not guaranteed, and as usual, if any value is null, it will be left out).\n\n```java\npublic class CustomPojo {\n  private final String name;\n  private final int number;\n  @Param(\"region_id\")\n  private final String regionId;\n\n  public CustomPojo (String name, int number, String regionId) {\n    this.name = name;\n    this.number = number;\n    this.regionId = regionId;\n  }\n}\n```\n\nTo setup a custom `QueryMapEncoder`:\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    MyApi myApi = Feign.builder()\n                 .queryMapEncoder(new MyCustomQueryMapEncoder())\n                 .target(MyApi.class, \"https:\u002F\u002Fapi.hostname.com\");\n  }\n}\n```\n\nWhen 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\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    MyApi myApi = Feign.builder()\n                 .queryMapEncoder(new BeanQueryMapEncoder())\n                 .target(MyApi.class, \"https:\u002F\u002Fapi.hostname.com\");\n  }\n}\n```\n\n### Error Handling\nIf you need more control over handling unexpected responses, Feign instances can\nregister a custom `ErrorDecoder` via the builder.\n\n```java\npublic class Example {\n  public static void main(String[] args) {\n    MyApi myApi = Feign.builder()\n                 .errorDecoder(new MyErrorDecoder())\n                 .target(MyApi.class, \"https:\u002F\u002Fapi.hostname.com\");\n  }\n}\n```\n\nAll responses that result in an HTTP status not in the 2xx range will trigger the `ErrorDecoder`'s `decode` method, allowing\nyou to handle the response, wrap the failure into a custom exception or perform any additional processing.\nIf you want to retry the request again, throw a `RetryableException`.  This will invoke the registered\n`Retryer`.\n\n### Retry\nFeign, by default, will automatically retry `IOException`s, regardless of HTTP method, treating them as transient network\nrelated exceptions, and any `RetryableException` thrown from an `ErrorDecoder`.  To customize this\nbehavior, register a custom `Retryer` instance via the builder.\n\nThe following example shows how to refresh token and retry with `ErrorDecoder` and `Retryer` when received a 401 response.\n\n```java\npublic class Example {\n    public static void main(String[] args) {\n        var github = Feign.builder()\n                .decoder(new GsonDecoder())\n                .retryer(new MyRetryer(100, 3))\n                .errorDecoder(new MyErrorDecoder())\n                .target(Github.class, \"https:\u002F\u002Fapi.github.com\");\n\n        var contributors = github.contributors(\"foo\", \"bar\", \"invalid_token\");\n        for (var contributor : contributors) {\n            System.out.println(contributor.login + \" \" + contributor.contributions);\n        }\n    }\n\n    static class MyErrorDecoder implements ErrorDecoder {\n\n        private final ErrorDecoder defaultErrorDecoder = new Default();\n\n        @Override\n        public Exception decode(String methodKey, Response response) {\n            \u002F\u002F wrapper 401 to RetryableException in order to retry\n            if (response.status() == 401) {\n                return new RetryableException(response.status(), response.reason(), response.request().httpMethod(), null, response.request());\n            }\n            return defaultErrorDecoder.decode(methodKey, response);\n        }\n    }\n\n    static class MyRetryer implements Retryer {\n\n        private final long period;\n        private final int maxAttempts;\n        private int attempt = 1;\n\n        public MyRetryer(long period, int maxAttempts) {\n            this.period = period;\n            this.maxAttempts = maxAttempts;\n        }\n\n        @Override\n        public void continueOrPropagate(RetryableException e) {\n            if (++attempt > maxAttempts) {\n                throw e;\n            }\n            if (e.status() == 401) {\n                \u002F\u002F remove Authorization first, otherwise Feign will add a new Authorization header\n                \u002F\u002F cause github responses a 400 bad request\n                e.request().requestTemplate().removeHeader(\"Authorization\");\n                e.request().requestTemplate().header(\"Authorization\", \"Bearer \" + getNewToken());\n                try {\n                    Thread.sleep(period);\n                } catch (InterruptedException ex) {\n                    throw e;\n                }\n            } else {\n                throw e;\n            }\n        }\n\n        \u002F\u002F Access an external api to obtain new token\n        \u002F\u002F In this example, we can simply return a fixed token to demonstrate how Retryer works\n        private String getNewToken() {\n            return \"newToken\";\n        }\n\n        @Override\n        public Retryer clone() {\n            return new MyRetryer(period, maxAttempts);\n        }\n}\n```\n\n`Retryer`s are responsible for determining if a retry should occur by returning either a `true` or\n`false` from the method `continueOrPropagate(RetryableException e);`  A `Retryer` instance will be\ncreated for each `Client` execution, allowing you to maintain state bewteen each request if desired.\n\nIf the retry is determined to be unsuccessful, the last `RetryException` will be thrown.  To throw the original\ncause that led to the unsuccessful retry, build your Feign client with the `exceptionPropagationPolicy()` option.\n\n#### Response Interceptor\nIf you need to treat what would otherwise be an error as a success and return a result rather than throw an exception then you may use a `ResponseInterceptor`.\n\nAs an example Feign includes a simple `RedirectionInterceptor` that can be used to extract the location header from redirection responses.\n```java\npublic interface Api {\n  \u002F\u002F returns a 302 response\n  @RequestLine(\"GET \u002Flocation\")\n  String location();\n}\n\npublic class MyApp {\n  public static void main(String[] args) {\n    \u002F\u002F Configure the HTTP client to ignore redirection\n    Api api = Feign.builder()\n                   .options(new Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, false))\n                   .responseInterceptor(new RedirectionInterceptor())\n                   .target(Api.class, \"https:\u002F\u002Fredirect.example.com\");\n  }\n}\n```\n\n### Metrics\nBy default, feign won't collect any metrics.\n\nBut, it's possible to add metric collection capabilities to any feign client.\n\nMetric Capabilities provide a first-class Metrics API that users can tap into to gain insight into the request\u002Fresponse lifecycle.\n\n> **A Note on Metrics modules**:\n>\n> All the metric-integrations are built in separate modules and not available in the `feign-core` module. You will need to add them to your dependencies.\n\n#### Dropwizard Metrics 4\n\n```java\npublic class MyApp {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n                         .addCapability(new Metrics4Capability())\n                         .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n\n    github.contributors(\"OpenFeign\", \"feign\");\n    \u002F\u002F metrics will be available from this point onwards\n  }\n}\n```\n\n#### Dropwizard Metrics 5\n\n```java\npublic class MyApp {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n                         .addCapability(new Metrics5Capability())\n                         .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n\n    github.contributors(\"OpenFeign\", \"feign\");\n    \u002F\u002F metrics will be available from this point onwards\n  }\n}\n```\n\n#### Micrometer\n\n```java\npublic class MyApp {\n  public static void main(String[] args) {\n    GitHub github = Feign.builder()\n                         .addCapability(new MicrometerCapability())\n                         .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n\n    github.contributors(\"OpenFeign\", \"feign\");\n    \u002F\u002F metrics will be available from this point onwards\n  }\n}\n```\n\n#### Static and Default Methods\nInterfaces targeted by Feign may have static or default methods (if using Java 8+).\nThese allows Feign clients to contain logic that is not expressly defined by the underlying API.\nFor example, static methods make it easy to specify common client build configurations; default methods can be used to compose queries or define default parameters.\n\n```java\ninterface GitHub {\n  @RequestLine(\"GET \u002Frepos\u002F{owner}\u002F{repo}\u002Fcontributors\")\n  List\u003CContributor> contributors(@Param(\"owner\") String owner, @Param(\"repo\") String repo);\n\n  @RequestLine(\"GET \u002Fusers\u002F{username}\u002Frepos?sort={sort}\")\n  List\u003CRepo> repos(@Param(\"username\") String owner, @Param(\"sort\") String sort);\n\n  default List\u003CRepo> repos(String owner) {\n    return repos(owner, \"full_name\");\n  }\n\n  \u002F**\n   * Lists all contributors for all repos owned by a user.\n   *\u002F\n  default List\u003CContributor> contributors(String user) {\n    MergingContributorList contributors = new MergingContributorList();\n    for(Repo repo : this.repos(owner)) {\n      contributors.addAll(this.contributors(user, repo.getName()));\n    }\n    return contributors.mergeResult();\n  }\n\n  static GitHub connect() {\n    return Feign.builder()\n                .decoder(new GsonDecoder())\n                .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n  }\n}\n```\n\n\n### Async execution via `CompletableFuture`\n\nFeign 10.8 introduces a new builder `AsyncFeign` that allow methods to return `CompletableFuture` instances.\n\n```java\ninterface GitHub {\n  @RequestLine(\"GET \u002Frepos\u002F{owner}\u002F{repo}\u002Fcontributors\")\n  CompletableFuture\u003CList\u003CContributor>> contributors(@Param(\"owner\") String owner, @Param(\"repo\") String repo);\n}\n\npublic class MyApp {\n  public static void main(String... args) {\n    GitHub github = AsyncFeign.builder()\n                         .decoder(new GsonDecoder())\n                         .target(GitHub.class, \"https:\u002F\u002Fapi.github.com\");\n\n    \u002F\u002F Fetch and print a list of the contributors to this library.\n    CompletableFuture\u003CList\u003CContributor>> contributors = github.contributors(\"OpenFeign\", \"feign\");\n    for (Contributor contributor : contributors.get(1, TimeUnit.SECONDS)) {\n      System.out.println(contributor.login + \" (\" + contributor.contributions + \")\");\n    }\n  }\n}\n```\n\nInitial implementation include 2 async clients:\n- `AsyncClient.Default`\n- `AsyncApacheHttp5Client`\n\n## Maven’s Bill of Material (BOM)\n\nKeeping all feign libraries on the same version is essential to avoid incompatible binaries. When consuming external dependencies, can be tricky to make sure only one version is present.\n\nWith that in mind, feign build generates a module called `feign-bom` that locks the versions for all `feign-*` modules.\n\nThe Bill Of Material is a special POM file that groups dependency versions that are known to be valid and tested to work together. This will reduce the developers’ pain of having to test the compatibility of different versions and reduce the chances to have version mismatches.\n\n\n[Here](https:\u002F\u002Frepo1.maven.org\u002Fmaven2\u002Fio\u002Fgithub\u002Fopenfeign\u002Ffeign-bom\u002F11.9\u002Ffeign-bom-11.9.pom) is one example of what feign BOM file looks like.\n\n#### Usage\n\n```xml\n\u003Cproject>\n\n...\n\n  \u003CdependencyManagement>\n    \u003Cdependencies>\n      \u003Cdependency>\n        \u003CgroupId>io.github.openfeign\u003C\u002FgroupId>\n        \u003CartifactId>feign-bom\u003C\u002FartifactId>\n        \u003Cversion>??feign.version??\u003C\u002Fversion>\n        \u003Ctype>pom\u003C\u002Ftype>\n        \u003Cscope>import\u003C\u002Fscope>\n      \u003C\u002Fdependency>\n    \u003C\u002Fdependencies>\n  \u003C\u002FdependencyManagement>\n\u003C\u002Fproject>\n```\n# Form Encoder\n\n[![build_status](https:\u002F\u002Ftravis-ci.org\u002FOpenFeign\u002Ffeign-form.svg?branch=master)](https:\u002F\u002Ftravis-ci.org\u002FOpenFeign\u002Ffeign-form)\n[![maven_central](https:\u002F\u002Fmaven-badges.herokuapp.com\u002Fmaven-central\u002Fio.github.openfeign.form\u002Ffeign-form\u002Fbadge.svg)](https:\u002F\u002Fmaven-badges.herokuapp.com\u002Fmaven-central\u002Fio.github.openfeign.form\u002Ffeign-form)\n[![License](http:\u002F\u002Fimg.shields.io\u002F:license-apache-brightgreen.svg)](http:\u002F\u002Fwww.apache.org\u002Flicenses\u002FLICENSE-2.0.html)\n\nThis module adds support for encoding **application\u002Fx-www-form-urlencoded** and **multipart\u002Fform-data** forms.\n\n## Add dependency\n\nInclude the dependency to your app:\n\n**Maven**:\n\n```xml\n\u003Cdependencies>\n  ...\n  \u003Cdependency>\n    \u003CgroupId>io.github.openfeign.form\u003C\u002FgroupId>\n    \u003CartifactId>feign-form\u003C\u002FartifactId>\n    \u003Cversion>4.0.0\u003C\u002Fversion>\n  \u003C\u002Fdependency>\n  ...\n\u003C\u002Fdependencies>\n```\n\n**Gradle**:\n\n```groovy\ncompile 'io.github.openfeign.form:feign-form:4.0.0'\n```\n\n## Requirements\n\nThe `feign-form` extension depend on `OpenFeign` and its *concrete* versions:\n\n- all `feign-form` releases before **3.5.0** works with `OpenFeign` **9.\\*** versions;\n- starting from `feign-form`'s version **3.5.0**, the module works with `OpenFeign` **10.1.0** versions and greater.\n\n> **IMPORTANT:** there is no backward compatibility and no any gurantee that the `feign-form`'s versions after **3.5.0** work with `OpenFeign` before **10.\\***. `OpenFeign` was refactored in 10th release, so the best approach - use the freshest `OpenFeign` and `feign-form` versions.\n\nNotes:\n\n- [spring-cloud-openfeign](https:\u002F\u002Fgithub.com\u002Fspring-cloud\u002Fspring-cloud-openfeign) uses `OpenFeign` **9.\\*** till **v2.0.3.RELEASE** and uses **10.\\*** after. Anyway, the dependency already has suitable `feign-form` version, see [dependency pom](https:\u002F\u002Fgithub.com\u002Fspring-cloud\u002Fspring-cloud-openfeign\u002Fblob\u002Fmaster\u002Fspring-cloud-openfeign-dependencies\u002Fpom.xml#L19), so you don't need to specify it separately;\n\n- `spring-cloud-starter-feign` is a **deprecated** dependency and it always uses the `OpenFeign`'s **9.\\*** versions.\n\n## Usage\n\nAdd `FormEncoder` to your `Feign.Builder` like so:\n\n```java\nSomeApi github = Feign.builder()\n                      .encoder(new FormEncoder())\n                      .target(SomeApi.class, \"http:\u002F\u002Fapi.some.org\");\n```\n\nMoreover, you can decorate the existing encoder, for example JsonEncoder like this:\n\n```java\nSomeApi github = Feign.builder()\n                      .encoder(new FormEncoder(new JacksonEncoder()))\n                      .target(SomeApi.class, \"http:\u002F\u002Fapi.some.org\");\n```\n\nAnd use them together:\n\n```java\ninterface SomeApi {\n\n  @RequestLine(\"POST \u002Fjson\")\n  @Headers(\"Content-Type: application\u002Fjson\")\n  void json (Dto dto);\n\n  @RequestLine(\"POST \u002Fform\")\n  @Headers(\"Content-Type: application\u002Fx-www-form-urlencoded\")\n  void from (@Param(\"field1\") String field1, @Param(\"field2\") String[] values);\n}\n```\n\nYou can specify two types of encoding forms by `Content-Type` header.\n\n### application\u002Fx-www-form-urlencoded\n\n```java\ninterface SomeApi {\n\n  @RequestLine(\"POST \u002Fauthorization\")\n  @Headers(\"Content-Type: application\u002Fx-www-form-urlencoded\")\n  void authorization (@Param(\"email\") String email, @Param(\"password\") String password);\n\n  \u002F\u002F Group all parameters within a POJO\n  @RequestLine(\"POST \u002Fuser\")\n  @Headers(\"Content-Type: application\u002Fx-www-form-urlencoded\")\n  void addUser (User user);\n\n  class User {\n\n    Integer id;\n\n    String name;\n  }\n}\n```\n\n### multipart\u002Fform-data\n\n```java\ninterface SomeApi {\n\n  \u002F\u002F File parameter\n  @RequestLine(\"POST \u002Fsend_photo\")\n  @Headers(\"Content-Type: multipart\u002Fform-data\")\n  void sendPhoto (@Param(\"is_public\") Boolean isPublic, @Param(\"photo\") File photo);\n\n  \u002F\u002F byte[] parameter\n  @RequestLine(\"POST \u002Fsend_photo\")\n  @Headers(\"Content-Type: multipart\u002Fform-data\")\n  void sendPhoto (@Param(\"is_public\") Boolean isPublic, @Param(\"photo\") byte[] photo);\n\n  \u002F\u002F FormData parameter\n  @RequestLine(\"POST \u002Fsend_photo\")\n  @Headers(\"Content-Type: multipart\u002Fform-data\")\n  void sendPhoto (@Param(\"is_public\") Boolean isPublic, @Param(\"photo\") FormData photo);\n\n  \u002F\u002F Group all parameters within a POJO\n  @RequestLine(\"POST \u002Fsend_photo\")\n  @Headers(\"Content-Type: multipart\u002Fform-data\")\n  void sendPhoto (MyPojo pojo);\n\n  class MyPojo {\n\n    @FormProperty(\"is_public\")\n    Boolean isPublic;\n\n    File photo;\n  }\n}\n```\n\nIn the example above, the `sendPhoto` method uses the `photo` parameter using three different supported types.\n\n* `File` will use the File's extension to detect the `Content-Type`;\n* `byte[]` will use `application\u002Foctet-stream` as `Content-Type`;\n* `FormData` will use the `FormData`'s `Content-Type` and `fileName`;\n* Client's custom POJO for grouping parameters (including types above).\n\n`FormData` is custom object that wraps a `byte[]` and defines a `Content-Type` and `fileName` like this:\n\n```java\n  FormData formData = new FormData(\"image\u002Fpng\", \"filename.png\", myDataAsByteArray);\n  someApi.sendPhoto(true, formData);\n```\n\n### Spring MultipartFile and Spring Cloud Netflix @FeignClient support\n\nYou can also use Form Encoder with Spring `MultipartFile` and `@FeignClient`.\n\nInclude the dependencies to your project's pom.xml file:\n\n```xml\n\u003Cdependencies>\n  \u003Cdependency>\n    \u003CgroupId>io.github.openfeign.form\u003C\u002FgroupId>\n    \u003CartifactId>feign-form\u003C\u002FartifactId>\n    \u003Cversion>4.0.0\u003C\u002Fversion>\n  \u003C\u002Fdependency>\n  \u003Cdependency>\n    \u003CgroupId>io.github.openfeign.form\u003C\u002FgroupId>\n    \u003CartifactId>feign-form-spring\u003C\u002FartifactId>\n    \u003Cversion>4.0.0\u003C\u002Fversion>\n  \u003C\u002Fdependency>\n\u003C\u002Fdependencies>\n```\n\n```java\n@FeignClient(\n    name = \"file-upload-service\",\n    configuration = FileUploadServiceClient.MultipartSupportConfig.class\n)\npublic interface FileUploadServiceClient extends IFileUploadServiceClient {\n\n  public class MultipartSupportConfig {\n\n    @Autowired\n    private ObjectFactory\u003CHttpMessageConverters> messageConverters;\n\n    @Bean\n    public Encoder feignFormEncoder () {\n      return new SpringFormEncoder(new SpringEncoder(messageConverters));\n    }\n  }\n}\n```\n\nOr, if you don't need Spring's standard encoder:\n\n```java\n@FeignClient(\n    name = \"file-upload-service\",\n    configuration = FileUploadServiceClient.MultipartSupportConfig.class\n)\npublic interface FileUploadServiceClient extends IFileUploadServiceClient {\n\n  public class MultipartSupportConfig {\n\n    @Bean\n    public Encoder feignFormEncoder () {\n      return new SpringFormEncoder();\n    }\n  }\n}\n```\n\nThanks to [tf-haotri-pham](https:\u002F\u002Fgithub.com\u002Ftf-haotri-pham) for his feature, which makes use of Apache commons-fileupload library, which handles the parsing of the multipart response. The body data parts are held as byte arrays in memory.\n\nTo use this feature, include SpringManyMultipartFilesReader in the list of message converters for the Decoder and have the Feign client return an array of MultipartFile:\n\n```java\n@FeignClient(\n    name = \"${feign.name}\",\n    url = \"${feign.url}\"\n    configuration = DownloadClient.ClientConfiguration.class\n)\npublic interface DownloadClient {\n\n  @RequestMapping(\"\u002Fmultipart\u002Fdownload\u002F{fileId}\")\n  MultipartFile[] download(@PathVariable(\"fileId\") String fileId);\n\n  class ClientConfiguration {\n\n    @Autowired\n    private ObjectFactory\u003CHttpMessageConverters> messageConverters;\n\n    @Bean\n    public Decoder feignDecoder () {\n      List\u003CHttpMessageConverter\u003C?>> springConverters =\n            messageConverters.getObject().getConverters();\n\n      List\u003CHttpMessageConverter\u003C?>> decoderConverters =\n            new ArrayList\u003CHttpMessageConverter\u003C?>>(springConverters.size() + 1);\n\n      decoderConverters.addAll(springConverters);\n      decoderConverters.add(new SpringManyMultipartFilesReader(4096));\n\n      HttpMessageConverters httpMessageConverters = new HttpMessageConverters(decoderConverters);\n\n      return new SpringDecoder(new ObjectFactory\u003CHttpMessageConverters>() {\n\n        @Override\n        public HttpMessageConverters getObject() {\n          return httpMessageConverters;\n        }\n      });\n    }\n  }\n}\n```\n","Feign 是一个简化了 Java HTTP 客户端编写过程的工具。它通过注解处理和模板化请求的方式，使得开发者能够以最小的代码量来调用HTTP API，并支持自定义解码器和错误处理机制，从而实现对任何基于文本的HTTP API的灵活对接。此外，Feigon还提供了诸如响应缓存、完整的URI模板表达式支持等功能，进一步增强了其在实际应用中的灵活性与效率。适用于需要频繁与外部服务进行HTTP交互的Java项目场景中，特别是对于追求简洁API客户端开发体验的团队而言，Feign是一个非常实用的选择。","2026-06-11 02:58:32","top_language"]