Don’t let WebClient’s bodyToMono trick you

Jeevjyot Singh Chhabda
2 min readJun 21, 2020

--

The spring-webflux module includes a non-blocking, reactive client for HTTP requests with Reactive Streams back pressure. WebClient is an interface and main entry point to perform non-blocking web requests. It provides great API’s to get the task done.

One of the very common method used to deserialize the request and convert to Mono is bodyToMono and it documentations reads — “Extract the body to a Mono.”

One can simply write

Mono<FooBar> fooBarRequest = request.bodyToMono(FooBarr.class);

convert the request to Mono of <T>.

Everything works great if body is present. When body is not present — .bodyToMono(Classz.class)emits an empty mono which is not very clear from the documentation. :whoaa: It is normal to expect that bodyToMono will emit an error when no body is present.

Let’s take an example

public Mono<ServerResponse> fooBarHandler(ServerRequest request) {
Mono<FooBar> fooBarMono = request.bodyToMono(FooBar.class);
return fooBarMono
.doOnEach(mdcLogger.info("processing the foorBar request."))
.then(ok().build());
}

//this our body and it is required
public class FooBar {
@NonNull
String id;
}

From the above code — one would expect that if body is empty, 400 BadRequest should be returned. But that doesn’t happen.

.bodyToMono() emits empty mono when there is no body is present. That is an expected behavior as Mono emits 0 or 1 element and an empty body just produces a mono that completes without emitting a value, much like Mono.empty(). And if this case is not handled, the above code ends up returning 200 OK which violates the rest standard as for the above scenario most suited is 400 BadRequest.

How do we make sure that it errors out when body is required?

There are a couple of ways it can be handled.

  1. We can make sure we always want Mono to emit 1 element, it can achieved by .single(). Documentation of single reads “Expect exactly one item from this Mono source or signal”
public Mono<ServerResponse> fooBarHandler(ServerRequest request) {
Mono<FooBar> fooBarMono = request.bodyToMono(FooBar.class)
.single();
return fooBarMono
.doOnEach(mdcLogger.info("processing the foorBar request."))
.then(ok().build());
}

The above code will error out if the body is empty with an error message “Source was empty”. You can handle in downstream and map it to BadRequest or whatever fits your bill.

2. You can handle the case using .switchIfEmpty ( I personally prefer this as it gives a more free hand to customized the solution.

Mono<FooBar> fooBarMono = request.bodyToMono(FooBar.class);
return fooBarMono
.doOnEach(mdcLogger.info("processing the request"))
.switchIfEmpty(Mono.error(new ServerWebInputException("Request body cannot be empty.")))
.then(ok().build());

The above code will return 400 Bad request

Spring-Webflux keeps surprising me.

Let me know if you’re handle this case differently.

--

--