Affects: Spring WebFlux latest


Hi.

The java example of the new exchangeToMono method does not compile.

https://docs.spring.io/spring-framework/docs/current/reference/html/web-reactive.html#webflux-client-exchange

ClientResponse.createException returns Mono. Even if one maps the flow still goes into doOnSuccess

client.get()
       .uri("/persons/1")
       .accept(MediaType.APPLICATION_JSON)
       .exchangeToMono(response -> {
           if (response.statusCode().equals(HttpStatus.OK)) {
               return response.bodyToMono(Person.class);
           }
           else if (response.statusCode().is4xxClientError()) {
               return response.bodyToMono(ErrorContainer.class);
           }
           else {
               return response.createException().map(exception -> Mono.error(exception));
               // return response.createException();
               // return Mono.error(response.createException());
           }
       })
       .doOnError(...)
       .doOnSuccess(...)
       .subscribe();

I just want to fail the sequence and precieve exception within Mono.error.

Comment From: rstoyanchev

Indeed there is an issue. response.createException() returns Mono<Exception> which want to turn into a Mono.error(ex) with the resolved exception, but we have to do that with .flatMap since otherwise .map ends up with Mono<Mono<?>>.

So it should be response.createException().flatMap(Mono::error).

Comment From: windymindy

Nice! But there is still something confusing. If one try and compile he would get 'incompatible types: inference variable T has incompatible bounds' 'no instance(s) of type variable(s) exist so that T2 conforms to T1 inference variable V has incompatible bounds: equality constaints: T1 lower bounds: T2' That's because of the exchangeToMono signature. And Person with ErrorContainer is not compatible. Maybe the type has to be erased with Mono<Object>.

This works though.

client.get()
       .uri("/persons/1")
       .accept(MediaType.APPLICATION_JSON)
       .exchangeToMono(response -> Mono.just(response)
           .flatMap(r -> {
               if (response.statusCode().equals(HttpStatus.OK))
                   return response.bodyToMono(Person.class);
               else if (response.statusCode().is4xxClientError())
                   return response.bodyToMono(ErrorContainer.class);
               else
                   return response.createException().map(exception -> Mono.error(exception));
           })
       )
       .doOnError(...)
       .doOnSuccess(...)
       .subscribe();

The signature difference is

<V> Mono<V> exchangeToMono(Function<ClientResponse, ? extends Mono<V>> var1)
<R> Mono<R> flatMap(Function<? super T, ? extends Mono<? extends R>> transformer)

@rstoyanchev

Comment From: rstoyanchev

It has to be Mono<Object> because the flatMap can return different objects depending on the response.