Affects: 5.2.13.RELEASE / Boot 2.3.9

What is wrong Using java.util.Optional (either as a class or ParameterizedTypeReference) in toEntity() of WebClient does not return an Optional.empty() but null when a given http response body is missing.

How it should work Empty http response bodies should create an Optional.empty() instead of a null value.

Research I dug into the WebClients logic and noticed that an empty Mono is not processed with the resolved HttpMessageDecoder (in my case, Jackson), e.g. in AbstractJackson2Decoder.decodeToMono and later hardcodes to null in WebClientUtils.mapToEntity via some... interesting magic strings :)

Example

// given an endpoint is reachable that will return no value as a body:
Optional<?> result = webClient.method(method).uri(uri).retrieve().toEntity(Optional.class).block();
// result is null, not Optional.empty

Comment From: rstoyanchev

While we could make this work, it would be more idiomatic to use Mono#blockOptional:

Optional<Foo> result = webClient.method(method).uri(uri).retrieve().toEntity(Foo.class).blockOptional();

Comment From: rstoyanchev

Generally where Mono is exposed or supported, e.g. in annotated controller methods, we don't support Optional as well because, because Mono already expresses the concept of empty and provides a seamless path to related JDK types.

Comment From: roookeee

While the reasoning might be correct, even the slighest abstraction layer above WebClient is forced to duplicate it's APIs for Optional and non Optional ParameterizedType<T> which is quite cumbersome, especially when you build libraries upon WebClient which don't want to leak that implementation detail. We currently have a workaround in place that "hacks" this behaviour by introspecting the supplied ParameterizedType<T> which is non-ideal.

Hope this clears stuff up :)

Thank you for your time!

Comment From: rstoyanchev

The context is more clear, but without any knowledge about the higher level API, it's not too clear where the duplication comes from. For such embedded use of WebClient, I imagine code paths trickling down to a single, generalized call to WebClient where an Optional wrapper can be handled as you described.

Adding support for Optional in all places where we decode to Flux<T> or Mono<T> could be a fairly extensive change, which is why I'm trying to make sure it is absolutely necessary.

Comment From: roookeee

Imagine a spring-data-repository implementation that is using WebClient internally. Instead of having one central function that does the HTTP calling we have to duplicate or do thorough type introspection (which isn't provided by Spring) to use different WebClient functions.

Spring supports Optional in a lot of places and it's just a surprising implementation detail which is why I created this issue: for consistencies sake.

That being said it seems like this is quite the niche issue. Throwing a lot of work at this seems unwise unless you also see merit in the broader implications of this (consistency, non-surprising APIs). Debugging this issue took quite some time so this issue existing is already worthwile.

Thank you for your time :)

Comment From: snicoll

It's been a while and there hasn't been additional interested by the community so I am going to close this now.