Using dynamic URL for Spring ReactiveFeignClient

3.3k views Asked by At

I'm using ReactiveFeignClient from Playtika I need to use dynamic URL especially for the host part because I want to use the same interface for several services that have the same request and response formats, but different host. The URLs on each service can have different host name and prefix, but all have the same suffix. For example:

  • http://localhost:3001/games/purchase
  • http://localhost:3002/gadgets/phone/purchase

Actually I don't know whether it has the same behavior as non-reactive feign client. I follow the suggestion on How can I change the feign URL during the runtime?.

Here's the client interface.

@ReactiveFeignClient(
    name = "dummy",
    configuration = TransactionClient.Configuration.class
)
public interface TransactionClient {

//  @PostMapping("/purchase") // Using @PostMapping and @RequestLine both don't work
  @RequestLine("POST /purchase")
  Mono<PurchaseResponseDto> doPurchase(
      URI baseUrl,
      @Valid @RequestBody PurchaseRequestDto requestDTO
  );

  @RequiredArgsConstructor
  class Configuration {

    @Bean
    public ReactiveStatusHandler reactiveStatusHandler() {
      return new CustomStatusHandler();
    }
  }
}

And here's the auto configuration

@Configuration
public class TransactionClientServiceAutoConfiguration {

  @Bean
  public Contract useFeignAnnotations() {
    return new Contract.Default();
  }

  @Bean
  @LoadBalanced
  public TransactionClient botRemoteClient() {
    return Feign.builder().target(Target.EmptyTarget.create(TransactionClient.class));
  }
}

However, I got error indicating that no service with name dummy. It's just a dummy name indeed, because the name parameter is required for @ReactiveFeignClient annotation and I want to use the interface for multiple services.

How to make dynamic url possible for @ReactiveFeignClient

2

There are 2 answers

0
Artabaz On

From reactive feign github I found this snippet:

IcecreamServiceApi client = 
         WebReactiveFeign  //WebClient based reactive feign  
        .<IcecreamServiceApi>builder()
        .target(IcecreamServiceApi.class, "http://www.myUrl.com")

You can change the url by creating a new instance of the client. Found no other way. Also, I added both @PostMapping and @RequestLine("POST") to the feign interface since I couldn't get the contracts option to work. Sharing this for posterity or until a better version comes along.

0
Marcus Voltolim On

Workaround with Java21 using ReactiveHttpRequestInterceptor and @RequestHeader:

@ReactiveFeignClient(name = "ReactiveDynamicUrlApi", url = BASE_URL, path = "v1/api/product", configuration = ReactiveDynamicUrlApi.RequestInterceptor.class)
interface ReactiveDynamicUrlApi {

    String BASE_URL = "base-url";

    @PostMapping(path = "{id}", headers = "Content-Type=application/json")
    Mono<String> post(@RequestHeader(BASE_URL) String baseUrl, @PathVariable String id, @RequestHeader String token, @RequestBody Map requestBody);


    class RequestInterceptor implements ReactiveHttpRequestInterceptor {

        @Override
        public Mono<ReactiveHttpRequest> apply(final ReactiveHttpRequest request) {
            var uri = URI.create(request.headers().get(BASE_URL).getFirst() + request.uri().getPath());
            request.headers().remove(BASE_URL);
            return Mono.just(new ReactiveHttpRequest(request, uri));
        }

    }

}