@@ -962,13 +962,78 @@ Feign, by default, will automatically retry `IOException`s, regardless of HTTP m
962
962
related exceptions, and any `RetryableException` thrown from an `ErrorDecoder`. To customize this
963
963
behavior, register a custom `Retryer` instance via the builder.
964
964
965
+ The following example shows how to refresh token and retry with `ErrorDecoder` and `Retryer` when received a 401 response.
966
+
965
967
```java
966
968
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
+ }
972
1037
}
973
1038
```
974
1039
0 commit comments