Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to have dynamic base URL with Quarkus MicroProfile Rest Client?

Quarkus using Rest Client, explains how to use the MicroProfile REST Client. For Base URL application.properties can be used.

org.acme.restclient.CountriesService/mp-rest/url=https://restcountries.eu/rest # 

With above approach, cant have dynamic base URL.

Able to achieve it by using RestClientBuilder as explained in MicroProfile Rest Client. Downside of this approach is not having auto-negotiation capability.

SimpleGetApi simpleGetApi = RestClientBuilder.newBuilder().baseUri(getApplicationUri()).build(SimpleGetApi.class);

Is there other or better way to achieve this? Thanks.

like image 849
Tushar Avatar asked Nov 15 '22 19:11

Tushar


1 Answers

While it is true, that the MP Rest CLient does not allow you to set the BaseUri dynamically when you use declarative/Injected clients, there are some (albeit hacky) ways how to achieve that.

One is to use standard ClientRequestFilter which can modify the URL:

@Provider
@Slf4j
public class Filter implements ClientRequestFilter {

  @Inject RequestScopeHelper helper;

  @Override
  public void filter(ClientRequestContext requestContext) throws IOException {
    if (helper.getUrl() != null) {
      URI newUri = URI.create(requestContext.getUri().toString().replace("https://originalhost.com", helper.getUrl()));
      requestContext.setUri(newUri);
    }
  }
}

Where RequestScopeHelper is some help class (e.g. request scoped bean) through which you can pass the dynamic url, for example:

@Inject
RequestScopeHelper helper;

@Inject
@RestClient
TestIface myApiClient;
 
public void callSomeAPIWithDynamicBaseUri(String dynamic) {
  helper.setUrl(dynamic);
  myApiClient.someMethod();
}

Second is to use MP rest client SPI, namely the RestClientListener which allows you to modify the rest clients after they are built.

For this to work, you have to set the scope of your rest client to RequestScoped so that new instance is created for each request(if you use singleton for example, then the client is only created once and your listener will only be called once). This you can do via quarkus properties:

quarkus.rest-client."com.example.MyRestIface".scope=javax.enterprise.context.RequestScoped
public class MyListener implements RestClientListener {

  @Override
  public void onNewClient(Class<?> serviceInterface, RestClientBuilder builder) {
    String newUri = //obtain dynamic URI from somewhere e.g. again request scope bean lookup, or maybe dynamic config source (create new in-memory ConfigSource, before you invoke your rest client set the corresponding rest client url property to your dynamic value, then inside this listener use ConfigProvider.getConfig().getProperty...)
    builder.baseUri(URI.create(newUri));
  }
}

Don't forget to register this listener as service provider(META-INF/services/org.eclipse.microprofile.rest.client.spi.RestClientListener)

Another option is to use custom CDI producer that would produce the Rest client instances for you; then you could control all client config yourself. You can use the RestClientBase from Quarkus rest client which is exactly what Quarkus uses under the hood during deployment phase to construct client instances. You will however have to duplicate all the logic related to registration of handlers, interceptors etc.

Do keep in mind, that any of these solutions will make the debugging and problem analysis more challenging - because you will now have multiple places, where the URI is controlled(MP config/quarkus properties, env vars, your custom impl...), so you need to be careful with your approach and maybe add some explicit log messages when you override the URI manually.

like image 136
yntelectual Avatar answered May 16 '23 07:05

yntelectual