Affects: 5.3.0

The return type of the exchangeToMono callback is ignored. Even worse, Spring is apparently abusing type erasure to return a type that is not the one declared as returning, so you end up with odd runtime errors. In short, you appear to always get a String, even if it's different then the declared return type in the exchangeToMono callback.

In short:

Web call like so

class DesiredReturnType {
        public final int statusCode;
        public final Mono<String> body;

        private DesiredReturnType(int statusCode, Mono<String> body) {
            this.statusCode = statusCode;
            this.body = body;
        }
    }
.....    
Mono<DesiredReturnType> ret = webClient.post()
                .uri(aUrl)
                .body(BodyInserters.fromValue(aRequestObject))
                .header(HttpHeaders.CONTENT_TYPE, MediaType.APPLICATION_JSON_VALUE)
                .exchangeToMono( resp -> callback(resp, someOtherVar) )
                .publishOn(Schedulers.fromExecutor(executorService));

callback like so:

public Mono<DesiredReturnType> callback(ClientResponse resp, SomeOther someOtherVar) {
      .... response code checking elided...
      return Mono.just( new DesiredReturnType( resp.rawStatusCode, resp.bodyToMono(String.class) ) );
}

Now, there's some code somewhere that deals with your response:

  Mono<DesiredReturnType> letsDoThis = ret.block();

Ok, now, any guesses on what type ret.block() returns? We declared it as the DesiredReturnType in:

Mono<DesiredReturnType> ret = webClient.post()
                .uri(aUrl)...

And, in fact, returned that type here:

return Mono.just( new DesiredReturnType( resp.rawStatusCode, resp.bodyToMono(String.class) ) );

And the compiler does indeed expect a Mono<DesiredReturnType> to be returned from .publishOn(Schedulers.fromExecutor(executorService));

SO.. ret.block() must return a...... String!!!! WHAT?

Yeah, that's what happens, Spring decided it was a going to return a String from the mono instead. Apparently whatever type you put in:

~resp.bodyToMono(String.class) is what is actually returned.~ Actually, I appear to always get a string back.

~In fact, whatever, resp.bodyToMono(String.class) is what gets returned by ret.block(), regardless of the actual type of ret (Mono<DesiredReturnType>)~

So now you get a java.lang.ClassCastException: class java.lang.String cannot be cast to class ....DesiredReturnType....

This is extremely counter-intuitive. You would not expect your type to be what is returned by some arbitrary method inside your callback instead of the actual, declared return type.

Also, it prevents you from enriching the data returned from ClientResponse downstream, which is what is the goal here, and it's super annoying that you can't do that.

Comment From: StuAtGit

Oh, even better, I updated to return Mono, and I tried this in my callback:

                response.bodyToMono(String.class);
                return Mono.just("HI FRIENDS");

Guess which string I get back? Not "HI FRIENDS"!

Comment From: StuAtGit

Nevermind. That's what happens when the webclient is mocked. sigh.