spring-cloud-openfeign icon indicating copy to clipboard operation
spring-cloud-openfeign copied to clipboard

Error implementing error decoder with RetryableException and path encoder

Open lucasoares opened this issue 3 years ago • 0 comments
trafficstars

Hello.

I'm creating a bean with an ErrorDecoder to implement a custom retry policy. I also have a Retryer bean.

My error decoder:

  @Bean
  public ErrorDecoder errorDecoder(OAuth2ClientContext oAuth2ClientContext) {
    Default errorDecoder = new Default();

    return (methodKey, response) -> {
      FeignException exception = FeignException.errorStatus(methodKey, response);

      if (<custom-check>) {
        return new RetryableException(
            response.status(),
            response.reason(),
            response.request().httpMethod(),
            exception,
            null,
            response.request());
      }

      return errorDecoder.decode(methodKey, response);
    };
  }

My retryer:

  @Bean("customFeignRetrier")
  public Retryer retryer() {
    return new Retryer.Default(this.period, this.maxPeriod, this.retriesLimit);
  }

Making a simple integration test to check the custom policy, when I force the first request to fail, the second throws this exception:

Caused by: java.lang.IllegalArgumentException: url values must be not be absolute.
	at feign.RequestTemplate.uri(RequestTemplate.java:434)
	at feign.RequestTemplate.uri(RequestTemplate.java:421)
	at mypackage.UrlEncodingInterceptor.apply(UrlEncodingInterceptor.java:14)
	at feign.SynchronousMethodHandler.targetRequest(SynchronousMethodHandler.java:161)
	at feign.SynchronousMethodHandler.executeAndDecode(SynchronousMethodHandler.java:110)
	at feign.SynchronousMethodHandler.invoke(SynchronousMethodHandler.java:89)

My feign client used in the test case:

@FeignClient(contextId = "Check", name = "check", url = "http://localhost:${server.port}")
public interface ClientCredentialsFeignClient {
  @GetMapping("/check")
  ResponseEntity<SecurityData> check();
}

My test case:

void test() {
    this.wireMockServer.stubFor(
        WireMock.get(WireMock.urlEqualTo("/check"))
            .willReturn(WireMock.aResponse().withStatus(401)));

    Assertions.assertThrows(FeignException.class, () -> this.client.check());
    
    // verify a mock I have to check if the request was retried
}

Debugging the test execution, the exception is thrown because of another custom interceptor my team are using:

/** Interceptor to fix url encoding of issue: https://github.com/OpenFeign/feign/issues/1190 */
public class UrlEncodingInterceptor implements RequestInterceptor {

  @SneakyThrows
  @Override
  public void apply(RequestTemplate template) {
    template.uri(URLDecoder.decode(template.path(), "UTF-8"));
  }
}

In the first request the template.path() appears to be only the path (/check in this case).. In the second request it is also returning the full URL of the request (http://localhost:1234/check in this case).

Why template.path() returns different values for each execution? Any workaround on this?

Versions:

spring-cloud-starter-openfeign - 3.1.1 (tried to update to 3.1.3 but same error)
feign-core - 11.8
spring-boot - 2.6.6
spring-cloud - 2021.0.1

lucasoares avatar Jun 25 '22 01:06 lucasoares