Skip to content

Commit a72bbcd

Browse files
xialeistudiostonexia
andauthored
doc: enrich example for ErrorDecoder and Retryer (#2030)
Co-authored-by: stonexia <[email protected]>
1 parent 19cd919 commit a72bbcd

File tree

1 file changed

+70
-5
lines changed

1 file changed

+70
-5
lines changed

README.md

Lines changed: 70 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -962,13 +962,78 @@ Feign, by default, will automatically retry `IOException`s, regardless of HTTP m
962962
related exceptions, and any `RetryableException` thrown from an `ErrorDecoder`. To customize this
963963
behavior, register a custom `Retryer` instance via the builder.
964964
965+
The following example shows how to refresh token and retry with `ErrorDecoder` and `Retryer` when received a 401 response.
966+
965967
```java
966968
public class Example {
967-
public static void main(String[] args) {
968-
MyApi myApi = Feign.builder()
969-
.retryer(new MyRetryer())
970-
.target(MyApi.class, "https://api.hostname.com");
971-
}
969+
public static void main(String[] args) {
970+
var github = Feign.builder()
971+
.decoder(new GsonDecoder())
972+
.retryer(new MyRetryer(100, 3))
973+
.errorDecoder(new MyErrorDecoder())
974+
.target(Github.class, "https://api.github.com");
975+
976+
var contributors = github.contributors("foo", "bar", "invalid_token");
977+
for (var contributor : contributors) {
978+
System.out.println(contributor.login + " " + contributor.contributions);
979+
}
980+
}
981+
982+
static class MyErrorDecoder implements ErrorDecoder {
983+
984+
private final ErrorDecoder defaultErrorDecoder = new Default();
985+
986+
@Override
987+
public Exception decode(String methodKey, Response response) {
988+
// wrapper 401 to RetryableException in order to retry
989+
if (response.status() == 401) {
990+
return new RetryableException(response.status(), response.reason(), response.request().httpMethod(), null, response.request());
991+
}
992+
return defaultErrorDecoder.decode(methodKey, response);
993+
}
994+
}
995+
996+
static class MyRetryer implements Retryer {
997+
998+
private final long period;
999+
private final int maxAttempts;
1000+
private int attempt = 1;
1001+
1002+
public MyRetryer(long period, int maxAttempts) {
1003+
this.period = period;
1004+
this.maxAttempts = maxAttempts;
1005+
}
1006+
1007+
@Override
1008+
public void continueOrPropagate(RetryableException e) {
1009+
if (++attempt > maxAttempts) {
1010+
throw e;
1011+
}
1012+
if (e.status() == 401) {
1013+
// remove Authorization first, otherwise Feign will add a new Authorization header
1014+
// cause github responses a 400 bad request
1015+
e.request().requestTemplate().removeHeader("Authorization");
1016+
e.request().requestTemplate().header("Authorization", "Bearer " + getNewToken());
1017+
try {
1018+
Thread.sleep(period);
1019+
} catch (InterruptedException ex) {
1020+
throw e;
1021+
}
1022+
} else {
1023+
throw e;
1024+
}
1025+
}
1026+
1027+
// Access an external api to obtain new token
1028+
// In this example, we can simply return a fixed token to demonstrate how Retryer works
1029+
private String getNewToken() {
1030+
return "newToken";
1031+
}
1032+
1033+
@Override
1034+
public Retryer clone() {
1035+
return new MyRetryer(period, maxAttempts);
1036+
}
9721037
}
9731038
```
9741039

0 commit comments

Comments
 (0)