I need to call an third party API which requires an authentication call beforehand to get an authentication token. The Authentication API is in json but the subsequent call is in XML.
I have separately :
webclient.post().uri("/auth").header(ACCEPT,JSON).retrieve()
.bodyToMono(AuthToken.class);
webclient.post().uri("/api").header(ACCEPT,XML).header("AUTH",authToken).retrive().bodyToFlux();
How should I implement the method to be able to access the second API?
I tried to assign a variable inside the method with token = firstCall.block()
but I've got block() is not supported
error.
You just have to transform the original flux like:
webclient.post().uri("/auth")
.header(ACCEPT,JSON)
.retrieve()
.bodyToMono(AuthToken.class)
.flatMapMany(authToken -> webclient.post().uri("/api")
.header(ACCEPT,XML)
.header("AUTH",authToken).retrive().bodyToFlux();
A better solution would be to use a ExchangeFilterFunction that will fetch the token for you https://docs.spring.io/spring-framework/docs/current/spring-framework-reference/web-reactive.html#webflux-client-filter
Something like that (not tested might have bug):
WebClient authWebClient = WebClient.builder().build();
WebClient webClient = WebClient.builder()
.filter(((request, next) -> authWebClient.post()
.uri("/auth")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(AuthToken.class)
.flatMap(authToken -> next.exchange(ClientRequest.from(request)
.headers(headers -> headers.add("AUTH", authToken))
.build()))
))
.build();
webClient.post().uri("/api")
.accept(MediaType.APPLICATION_XML)
.retrieve()
.bodyToFlux(MyData.class);
This is basic but you could add cache to avoid requesting or fetch again if token is expired... Be aware that builtin ExchangeFilterFunction exists for basic oauth2...
Wrap everything with a spring configuration:
@Configuration
public class WebClientConfiguration {
@Bean
public WebClient authWebClient(final WebClient.Builder webClientBuilder) {
return webClientBuilder.build();
}
@Bean
public ExchangeFilterFunction authFilter(final WebClient authWebClient) {
return (request, next) -> authWebClient.post()
.uri("/auth")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(AuthToken.class)
.flatMap(authToken -> next.exchange(ClientRequest.from(request)
.headers(headers -> headers.add("AUTH", authToken.toString()))
.build()));
}
@Bean
public WebClient webClient(final WebClient.Builder webClientBuilder, final ExchangeFilterFunction authFilter) {
return webClientBuilder
.filter(authFilter)
.build();
}
}
Or if you want to avoid lambda:
@Configuration
public class WebClientConfiguration {
@Bean
public WebClient authWebClient(final WebClient.Builder webClientBuilder) {
return webClientBuilder.build();
}
@Bean
public WebClient webClient(final WebClient.Builder webClientBuilder, final AuthFilter authFilter) {
return webClientBuilder
.filter(authFilter)
.build();
}
@Bean
public AuthFilter authFilter(WebClient authWebClient) {
return new AuthFilter(authWebClient);
}
}
public class AuthFilter implements ExchangeFilterFunction {
private final WebClient authWebClient;
public AuthFilter(WebClient authWebClient) {
this.authWebClient = authWebClient;
}
@Override
public Mono<ClientResponse> filter(final ClientRequest request, final ExchangeFunction next) {
return authWebClient.post()
.uri("/auth")
.accept(MediaType.APPLICATION_JSON)
.retrieve()
.bodyToMono(AuthToken.class)
.flatMap(authToken -> next.exchange(ClientRequest.from(request)
.headers(headers -> headers.add("AUTH", authToken.toString()))
.build()));
}
}