Feign is basically used
1. Function Overview
2. Custom error handling
Feign (default) throws only FeignException exceptions for all error cases, but if you want to handle a particular exception, Can pass in Feign. Builder. ErrorDecoder () implementation Feign. Codec. ErrorDecoder interface to achieve a goal.
Example:
Custom error handling
public class StashErrorDecoder implements ErrorDecoder {
@Override
public Exception decode(String methodKey, Response response) {
if (response.status() >= 400 && response.status() <= 499) {
return new StashClientException(
response.status(),
response.reason()
);
}
if (response.status() >= 500 && response.status() <= 599) {
return new StashServerException(
response.status(),
response.reason()
);
}
returnerrorStatus(methodKey, response); }}Copy the code
Apply error handling
return Feign.builder()
.errorDecoder(new StashErrorDecoder())
.target(StashApi.class, url);
Copy the code
3. Basic usage
Example:
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 + ")"); }}}Copy the code
3.1. Interface annotations
Feign’s Contract defines annotations that specify how the underlying client and interface work together.
Feign’s default Contract defines the following annotations:
annotations | Interface objects | usage |
---|---|---|
@RequestLine | Method | Define HttpMethod and UriTemplate for the request. Expression, wrapped in curly braces {expression}, parsed with its corresponding @param annotation argument. |
@Param | Parameter | Defines a template variable whose value will be used to parse the corresponding template expression, provided by name as a comment value. If a value is missing, it will try to get the name from the bytecode method parameter name (if the code was compiled with the -parameters flag). |
@Headers | Method, Type | Define a HeaderTemplate; Variant of UriTemplate. Use the @param annotation value to parse the corresponding expression. When used on Type, the template is applied to each request. When used on methods, templates will only apply to annotated methods. |
@QueryMap | Parameter | Defines a Map or POJO of name-value pairs that are expanded to a query string. |
@HeaderMap | Parameter | Define a Map of name-value pairs to extend Http Headers. |
@Body | Method | Define a template, similar to UriTemplate and HeaderTemplate, that uses the @param annotation value to parse the corresponding expression. |
3.2. Templates and expressions
A Feign expression represents a simple string expression (Level 1) defined by URI template-RFC 6570. The expression is extended with its corresponding Param annotation method parameter.
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;
intcontributions; }}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"); }}Copy the code
Expressions must be enclosed in braces {}, and can contain regular expression patterns separated by colons: to limit the values parsed. Example owners must be listed alphabetically. {owner: [a zA – Z] *}
3.3 request parameter extension
The RequestLine and QueryMap templates follow the URI template-RFC 6570 Level 1 Template specification, which specifies the following:
- Unparsed expressions are omitted.
- All literal and variable values are PCT encoded, if not already encoded or marked with the @param annotation.
3.4 undefined and null values
An undefined expression is an expression whose value is explicitly NULL or has no value supplied. According to URI template-RFC 6570, expressions can be supplied with null values. When Feign parses the expression, it first determines whether the value is defined, and if so, the query parameters are retained. If the expression is not defined, the query parameter is deleted. See the complete breakdown below.
An empty string
public void test(a) {
Map<String, Object> parameters = new LinkedHashMap<>();
parameters.put("param"."");
this.demoClient.test(parameters);
}
Copy the code
The results of
http://localhost:8080/test? param=
Copy the code
The loss of
public void test(a) {
Map<String, Object> parameters = new LinkedHashMap<>();
this.demoClient.test(parameters);
}
Copy the code
The results of
http://localhost:8080/test
Copy the code
undefined
public void test(a) {
Map<String, Object> parameters = new LinkedHashMap<>();
parameters.put("param".null);
this.demoClient.test(parameters);
}
Copy the code
The results of
http://localhost:8080/test
Copy the code
3.5. Custom extensions
The @param annotation has an optional attribute extender that allows full control over the extension of a single parameter. The Expander property must reference a class that implements the Expander interface:
public interface Expander {
String expand(Object value);
}
Copy the code
3.6. Request header extension
The Headers and HeaderMap templates follow the same rules as the request parameter extension, but with the following variations:
- Unparsed expressions are omitted. If the result is an empty header value, the entire header is deleted.
- PCT encoding is not performed.
Note about the @param parameter and its name:
All expressions with the same name, regardless of their position on @RequestLine, @QueryMap, @bodyTemplate, or @HEADERS, will resolve to the same value. In the following example, the value of contentType will be used to parse header and path expressions:
public interface ContentService {
@RequestLine("GET /api/documents/{contentType}")
@Headers("Accept: {contentType}")
String getDocumentByType(@Param("contentType") String type);
}
Copy the code
3.7 request body extension
The body template follows the same rules as the request parameter extension, but with the following changes:
- Unparsed expressions are omitted.
- The extended value is not passed through the encoder until it is placed in the request body.
- The Content-Type header must be specified.
3.8, custom
Feign has several aspects that can be customized. For simple cases, you can use feign.Builder () to build an API interface with custom components. For request Settings, You can do this using options(request.options options) on target() ConnectTimeout, connectTimeoutUnit, readTimeout, readTimeoutUnit, followRedirects. Such as:
interface Bank {
@RequestLine("POST /account/{id}")
Account getAccountInfo(@Param("id") String id);
}
public class BankService {
public static void main(String[] args) {
Bank bank = Feign.builder()
.decoder(new AccountDecoder())
.options(new Request.Options(10, TimeUnit.SECONDS, 60, TimeUnit.SECONDS, true))
.target(Bank.class, "https://api.examplebank.com"); }}Copy the code
3.9. Multiple Interfaces
Feign can generate multiple API interfaces. These are defined as targets (default HardCodedTarget) and allow requests to be discovered and decorated dynamically before execution. For example, the following pattern might decorate each request with the current URL and authentication token from the identity service.
public class CloudService {
public static void main(String[] args) {
CloudDNS cloudDNS = Feign.builder()
.target(new CloudIdentityTarget<CloudDNS>(user, apiKey));
}
class CloudIdentityTarget extends Target<CloudDNS> {
/* implementation of a Target */}}Copy the code
3.10, integration,
3.10.1, Gson
Gson includes an encoder and decoder that you can use with the JSON API. Add GsonEncoder and/or GsonDecoder to Feign.Builder like this:
public class Example {
public static void main(String[] args) {
GsonCodec codec = new GsonCodec();
GitHub github = Feign.builder()
.encoder(new GsonEncoder())
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com"); }}Copy the code
3.10.2, Jackson
Jackson includes an encoder and decoder that you can use with the JSON API. Add JacksonEncoder and/or JacksonDecoder to your Feign.Builder as follows:
public class Example {
public static void main(String[] args) {
GitHub github = Feign.builder()
.encoder(new JacksonEncoder())
.decoder(new JacksonDecoder())
.target(GitHub.class, "https://api.github.com"); }}Copy the code
3.10.3, Sax
SaxDecoder allows you to decode XML in a way that is compatible with normal JVM and Android environments. Here is an example of how to configure Sax response parsing:
public class Example {
public static void main(String[] args) {
Api api = Feign.builder()
.decoder(SAXDecoder.builder()
.registerContentHandler(UserIdHandler.class)
.build())
.target(Api.class, "https://apihost"); }}Copy the code
3.10.4, JAXB
JAXB includes encoders and decoders that can be used with the XML API. Add JAXBEncoder and/or JAXBDecoder to your Feign.Builder as follows:
public class Example {
public static void main(String[] args) {
Api api = Feign.builder()
.encoder(new JAXBEncoder())
.decoder(new JAXBDecoder())
.target(Api.class, "https://apihost"); }}Copy the code
3.10.5, JAX – RS
The JAXRSContract overrides annotation processing to use the standard processing provided by the JAX-RS specification. This is the current goal for the 1.1 specification. Here is the example rewritten above to use JAX-RS:
interface GitHub {
@GET @Path("/repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@PathParam("owner") String owner, @PathParam("repo") String repo);
}
public class Example {
public static void main(String[] args) {
GitHub github = Feign.builder()
.contract(new JAXRSContract())
.target(GitHub.class, "https://api.github.com"); }}Copy the code
3.10.6, OkHttp
OkHttpClient directs Feign’s HTTP requests to OkHttp for SPDY and better network control. To use OkHttp with Feign, add the OkHttp module to your classpath. Then, configure Feign to use OkHttpClient:
public class Example {
public static void main(String[] args) {
GitHub github = Feign.builder()
.client(new OkHttpClient())
.target(GitHub.class, "https://api.github.com"); }}Copy the code
3.10.7, Ribbon,
RibbonClient covers THE URL resolution of the Feign client, increasing the intelligent routing and elastic capabilities provided by the Ribbon. Integration requires that you pass the ribbon client name as the host part of the URL, such as myAppProd.
public class Example {
public static void main(String[] args) {
MyService api = Feign.builder()
.client(RibbonClient.create())
.target(MyService.class, "https://myAppProd"); }}Copy the code
3.10.8, Java 11 Http2
Http2Client directs Feign’s HTTP requests to the Java11 New HTTP/2 Client that implements HTTP/2. To use the new HTTP/2 client with Feign, use the Java SDK 11. Then, configure Feign to use Http2Client:
GitHub github = Feign.builder()
.client(new Http2Client())
.target(GitHub.class, "https://api.github.com");
Copy the code
3.10.9, Hystrix
HystrixFeign is configured with circuit breaker support provided by Hystrix. To use Hystrix with Feign, add the Hystrix module to your classpath. Then use the HystrixFeign builder:
public class Example {
public static void main(String[] args) {
MyService api = HystrixFeign.builder().target(MyService.class, "https://myAppProd"); }}Copy the code
3.10.10, SOAP
SOAP includes encoders and decoders that can be used with XML apis. This module adds support for encoding and decoding SOAP Body objects through JAXB and SOAPMessage. It USES to pack them into the original javax.mail. XML. Ws. Soap. The SOAPFaultException to provide SOAPFault decoding function, so you need to capture a SOAPFaultException can handle SOAPFault. Add SOAPEncoder and/or SOAPDecoder to your Feign.Builder like this:
public class Example {
public static void main(String[] args) {
Api api = Feign.builder()
.encoder(new SOAPEncoder(jaxbFactory))
.decoder(new SOAPDecoder(jaxbFactory))
.errorDecoder(new SOAPErrorDecoder())
.target(MyApi.class, "http://api"); }}Copy the code
3.10.11, SLF4J
SLF4JModule allows you to direct Feign logging to SLF4J, allowing you to easily use the logging back end of your choice (Logback, Log4J, and so on) to use SLF4J with Feign, Add the SLF4J module and the SLF4J binding of your choice to your classpath. Then, configure Feign to use the Slf4jLogger:
public class Example {
public static void main(String[] args) {
GitHub github = Feign.builder()
.logger(new Slf4jLogger())
.logLevel(Level.FULL)
.target(GitHub.class, "https://api.github.com"); }}Copy the code
3.11. Decoder
Feign.builder() allows you to specify additional configurations, such as how to decode the response. If any method in the interface returns a type other than Response, String, byte[], or void, you need to configure a non-default decoder. Here’s how to configure JSON decoding (using the Feign-Gson extension) :
public class Example {
public static void main(String[] args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com"); }}Copy the code
If you need to preprocess the response before feeding it to the decoder, you can use the mapAndDecode builder method. An example use case is dealing with jSONP-only apis, which you might need to untangle before sending to the Json decoder of your choice:
public class Example {
public static void main(String[] args) {
JsonpApi jsonpApi = Feign.builder()
.mapAndDecode((response, type) -> jsopUnwrap(response, type), new GsonDecoder())
.target(JsonpApi.class, "https://some-jsonp-api.com"); }}Copy the code
3.12. Encoder
The simplest way to send the request body to the server is to define a POST method that takes a String or byte[] argument without any comments. You may need to add a Content-Type header.
interface LoginClient {
@RequestLine("POST /")
@Headers("Content-Type: application/json")
void login(String content);
}
public class Example {
public static void main(String[] args) {
client.login("{\"user_name\": \"denominator\", \"password\": \"secret\"}"); }}Copy the code
By configuring the encoder, you can send type-safe request bodies. Here is an example using the Feign-Gson extension:
static class Credentials {
final String user_name;
final String password;
Credentials(String user_name, String password) {
this.user_name = user_name;
this.password = password; }}interface LoginClient {
@RequestLine("POST /")
void login(Credentials creds);
}
public class Example {
public static void main(String[] args) {
LoginClient client = Feign.builder()
.encoder(new GsonEncoder())
.target(LoginClient.class, "https://foo.com");
client.login(new Credentials("denominator"."secret")); }}Copy the code
3.13. @body Template
The @body annotation indicates a template that is extended with the @param annotation’s parameters. You may need to add a Content-Type header.
interface LoginClient {
@RequestLine("POST /")
@Headers("Content-Type: application/xml")
@Body("<login \"user_name\"=\"{user_name}\" \"password\"=\"{password}\"/>")
void xml(@Param("user_name") String user, @Param("password") String password);
@RequestLine("POST /")
@Headers("Content-Type: application/json")
// json curly braces must be escaped!
@Body("%7B\"user_name\": \"{user_name}\", \"password\": \"{password}\"%7D")
void json(@Param("user_name") String user, @Param("password") String password);
}
public class Example {
public static void main(String[] args) {
client.xml("denominator"."secret"); // <login "user_name"="denominator" "password"="secret"/>
client.json("denominator"."secret"); // {"user_name": "denominator", "password": "secret"}}}Copy the code
3.14. Request headers
Feign supports setting headers for requests either as part of the API or as part of the client, depending on the use case.
3.14.1. Set the title using apis
Defining headers as part of the API makes sense in cases where a particular interface or call should always set some header value. Static Headers can be set on an API interface or method using the @headers annotation.
Static Headers can be set on an API interface or method using the @headers annotation.
@Headers("Accept: application/json")
interface BaseApi<V> {
@Headers("Content-Type: application/json")
@RequestLine("PUT /api/{key}")
void put(@Param("key") String key, V value);
}
Copy the code
Method can specify dynamic content for a static header using a variable extension in @headers.
public interface Api {
@RequestLine("POST /")
@Headers("X-Ping: {token}")
void post(@Param("token") String token);
}
Copy the code
If the header field keys and values are dynamic, and perhaps the key range is not known in advance and may vary from method call to method call in the same API/client (e.g., custom metadata header fields such as “X-AMz-meta -” or” X-GOOG-meta -“), Map parameters can be annotated using HeaderMap to construct queries that use Map content as their header parameters.
public interface Api {
@RequestLine("POST /")
void post(@HeaderMap Map<String, Object> headerMap);
}
Copy the code
These methods specify header entries as part of the API and do not require any customization when building the Feign client.
3.14.2. Set request headers for each target
To customize the request header for each request method on Target, use the RequestInterceptor. RequestInterceptors can be shared across Target instances and should be thread-safe. RequestInterceptors apply to all request methods on Target. If you need to customize by method, you need to customize Target, because the RequestInterceptor doesn’t have access to the current method metadata. For an example of using RequestInterceptor to set the header, see the RequestInterceptor section. The request header can be set as part of a custom target.
static class DynamicAuthTokenTarget<T> implements Target<T> {
public DynamicAuthTokenTarget(Class
clazz, UrlAndTokenProvider provider, ThreadLocal
requestIdProvider)
;
@Override
public Request apply(RequestTemplate input) {
TokenIdAndPublicURL urlAndToken = provider.get();
if (input.url().indexOf("http") != 0) {
input.insert(0, urlAndToken.publicURL);
}
input.header("X-Auth-Token", urlAndToken.tokenId);
input.header("X-Request-ID", requestIdProvider.get());
returninput.request(); }}public class Example {
public static void main(String[] args) {
Bank bank = Feign.builder()
.target(newDynamicAuthTokenTarget(Bank.class, provider, requestIdProvider)); }}Copy the code
These methods depend on the custom RequestInterceptor or Target set on the Feign client when the Feign client is built, and can be used as a method to set headers on all API calls on a per-client basis. This is useful for performing operations such as setting authentication tokens in the headers of all API requests per client. When the call API calls on the thread API calls to run these methods, this allows in a context-specific manner in invoked dynamically set header – for example, thread local storage can be used for setting different header value according to the calling thread, this setting thread, such as for specific tracking identifier is very useful things.
4. Advanced usage
4.1. Basic Apis
In many cases, service apis follow the same conventions. Feign supports this pattern through a single inheritance interface. Consider this example:
interface BaseAPI {
@RequestLine("GET /health")
String health(a);
@RequestLine("GET /all")
List<Entity> all(a);
}
Copy the code
You can define and locate specific apis, inheriting base methods.
interface CustomAPI extends BaseAPI {
@RequestLine("GET /custom")
String custom(a);
}
Copy the code
In many cases, resource representations are also consistent. Therefore, the basic API interface supports type parameters.
@Headers("Accept: application/json")
interface BaseApi<V> {
@RequestLine("GET /api/{key}")
V get(@Param("key") String key);
@RequestLine("GET /api")
List<V> list(a);
@Headers("Content-Type: application/json")
@RequestLine("PUT /api/{key}")
void put(@Param("key") String key, V value);
}
interface FooApi extends BaseApi<Foo> {}interface BarApi extends BaseApi<Bar> {}Copy the code
4.2. Log Recording
You can set up Logger to log HTTP messages to and from the target. This is the easiest way:
public class Example {
public static void main(String[] args) {
GitHub github = Feign.builder()
.decoder(new GsonDecoder())
.logger(new Logger.JavaLogger("GitHub.Logger").appendToFile("logs/http.log"))
.logLevel(Logger.Level.FULL)
.target(GitHub.class, "https://api.github.com"); }}Copy the code
Note about JavaLogger: Avoid using the default JavaLogger() constructor – it is marked deprecated and will be removed soon.
4.3 request interceptor
When you need to change all requests, regardless of their target, you need to configure a RequestInterceptor. For example, if you act 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"); }}public class Example {
public static void main(String[] args) {
Bank bank = Feign.builder()
.decoder(accountDecoder)
.requestInterceptor(new ForwardedForInterceptor())
.target(Bank.class, "https://api.examplebank.com"); }}Copy the code
Interceptor is another common example of authentication, such as using the built-in BasicAuthRequestInterceptor.
public class Example {
public static void main(String[] args) {
Bank bank = Feign.builder()
.decoder(accountDecoder)
.requestInterceptor(new BasicAuthRequestInterceptor(username, password))
.target(Bank.class, "https://api.examplebank.com"); }}Copy the code
4.4. Customize @param extensions
Parameters using Param annotations are expanded based on their toString. The user can control this behavior, such as formatting dates, by specifying custom param.expander.
public interface Api {
@RequestLine("GET /? since={date}") Result list(@Param(value = "date", expander = DateToMillis.class) Date date);
}
Copy the code
4.5 dynamic Query parameters
You can use QueryMap to annotate Map parameters to construct queries that use Map content as their query parameters.
public interface Api {
@RequestLine("GET /find")
V find(@QueryMap Map<String, Object> queryMap);
}
Copy the code
This can also be used to generate query parameters from POJO objects using QueryMapEncoder.
public interface Api {
@RequestLine("GET /find")
V find(@QueryMap CustomPojo customPojo);
}
Copy the code
When used in this way, a query map is generated using the member variable name as the query parameter name, without specifying a custom QueryMapEncoder. The following POJO will generate the “/find? Name ={name}&number={number} “query parameters (the order of included query parameters is not guaranteed, as usual, if any value is null, it will be excluded).
public class CustomPojo {
private final String name;
private final int number;
public CustomPojo (String name, int number) {
this.name = name;
this.number = number; }}Copy the code
To set custom QueryMapEncoder:
public class Example {
public static void main(String[] args) {
MyApi myApi = Feign.builder()
.queryMapEncoder(new MyCustomQueryMapEncoder())
.target(MyApi.class, "https://api.hostname.com"); }}Copy the code
When you annotate an object with @QueryMap, the default encoder uses reflection to examine the supplied object field to expand the object value into a query string. If you want to build query strings using getter and setter methods defined in the Java Beans API, use BeanQueryMapEncoder
public class Example {
public static void main(String[] args) {
MyApi myApi = Feign.builder()
.queryMapEncoder(new BeanQueryMapEncoder())
.target(MyApi.class, "https://api.hostname.com"); }}Copy the code
4.6. Error handling
If you need more control over handling unexpected responses, the Feign instance can register a custom ErrorDecoder through the builder.
public class Example {
public static void main(String[] args) {
MyApi myApi = Feign.builder()
.errorDecoder(new MyErrorDecoder())
.target(MyApi.class, "https://api.hostname.com"); }}Copy the code
Any response that causes the HTTP state to fall outside the 2XX range will trigger the Decode method of ErrorDecoder, allowing you to process the response, wrap the failure in a custom exception, or perform any other processing. If you want to retry the request again, throw a RetryableException. This invokes the registered retrayer.
4.7 try again,
By default, Feign automatically retries IOExceptions regardless of the HTTP method, treating them as net-related transient exceptions and any Retryableexceptions thrown from the ErrorDecoder. To customize this behavior, register a custom Retryer instance through the builder.
public class Example {
public static void main(String[] args) {
MyApi myApi = Feign.builder()
.retryer(new MyRetryer())
.target(MyApi.class, "https://api.hostname.com"); }}Copy the code
The retries are responsible for propagate (RetryableException E) via continueOrPropagate(RetryableException E) from the method; Returns true or false to determine whether a retry should be performed; A Retryer instance will be created for each client execution, allowing you to maintain the state between each request as needed. If it is determined that the retry was unsuccessful, the last RetryException is thrown. Would like to sell the original reason that lead to retry failed, please use exceptionPropagationPolicy () option to build your Feign client.
Index of 4.8,
By default, Feign does not collect any metrics. However, metric collection can be added to any Feign client. Metric Capabilities provides a first-class Metrics API that users can leverage to gain insight into the request/response life cycle.
Dropwizard Metrics 4
public class MyApp {
public static void main(String[] args) {
GitHub github = Feign.builder()
.addCapability(new Metrics4Capability())
.target(GitHub.class, "https://api.github.com");
github.contributors("OpenFeign"."feign");
// metrics will be available from this point onwards}}Copy the code
Dropwizard Metrics 5
public class MyApp {
public static void main(String[] args) {
GitHub github = Feign.builder()
.addCapability(new Metrics5Capability())
.target(GitHub.class, "https://api.github.com");
github.contributors("OpenFeign"."feign");
// metrics will be available from this point onwards}}Copy the code
Micrometer
public class MyApp {
public static void main(String[] args) {
GitHub github = Feign.builder()
.addCapability(new MicrometerCapability())
.target(GitHub.class, "https://api.github.com");
github.contributors("OpenFeign"."feign");
// metrics will be available from this point onwards}}Copy the code
4.9. Static and default methods
The interface Feign is targeting may have static or default methods (if Java 8+ is used). These allow Feign clients to include logic that is not explicitly defined by the underlying API. For example, static methods can easily specify common client build configurations; The default methods can be used to combine queries or define default parameters.
interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
List<Contributor> contributors(@Param("owner") String owner, @Param("repo") String repo);
@RequestLine("GET /users/{username}/repos? sort={sort}")
List<Repo> repos(@Param("username") String owner, @Param("sort") String sort);
default List<Repo> repos(String owner) {
return repos(owner, "full_name");
}
/** * Lists all contributors for all repos owned by a user. */
default List<Contributor> contributors(String user) {
MergingContributorList contributors = new MergingContributorList();
for(Repo repo : this.repos(owner)) {
contributors.addAll(this.contributors(user, repo.getName()));
}
return contributors.mergeResult();
}
static GitHub connect(a) {
return Feign.builder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com"); }}Copy the code
4.10. Execute asynchronously through CompletableFuture
Feign 10.8 introduces a new builder AsyncFeign that allows methods to return CompletableFuture instances.
interface GitHub {
@RequestLine("GET /repos/{owner}/{repo}/contributors")
CompletableFuture<List<Contributor>> contributors(@Param("owner") String owner, @Param("repo") String repo);
}
public class MyApp {
public static void main(String... args) {
GitHub github = AsyncFeign.asyncBuilder()
.decoder(new GsonDecoder())
.target(GitHub.class, "https://api.github.com");
// Fetch and print a list of the contributors to this library.
CompletableFuture<List<Contributor>> contributors = github.contributors("OpenFeign"."feign");
for (Contributor contributor : contributors.get(1, TimeUnit.SECONDS)) {
System.out.println(contributor.login + "(" + contributor.contributions + ")"); }}}Copy the code
The initial implementation includes two asynchronous clients:
AsyncClient.Default
AsyncApacheHttp5Client