javaspring-webfluxproject-reactorspring-webclientwebflux

How to subscribe inside a WebFlux Controller


Inside a REST controller, I need to call a REST to obtain a value to be used as a URI variable to a second REST call.

@PostMapping
public void abbina(@RequestBody DocumentsUploadRequest documentsUploadRequest) {

  Mono<GetResult> result = WebClient
      .create(url)
      .get()
       .....
      .retrieve()
      .bodyToMono(GetResult.class)
      ;

  WebClient.post()
      .uri(...)
      .path("/{partita}")
      .build(result.block().getValue()))
      .....
      .bodyToMono(PostResult.class)
   ....
}

The problem is that inside a WebFlux REST isn't possibile call block on a mono/flux.

The code throw

java.lang.IllegalStateException block()/blockFirst()/blockLast() are blocking, which is not supported in thread reactor-http

I tried to change

.build(result.block().getValue()))

with

.build(result.share().block().getValue()))

But now the problem is that result.share().block() hangs indefinitely.


Solution

  • First of all, you should never block within a reactive pipeline. You should subscribe instead. In that particular case, the Spring Webflux framework will subscribe for you as long as you provide your publisher. To achieve this, the controller method has to return your Mono publisher like this:

    @PostMapping
    public Mono<Void> abbina(@RequestBody Attribute documentsUploadRequest) {
    
    }
    

    Here, the Mono<Void> defines that your publisher will complete without any value.

    Then you have to build a reactive pipeline without blocking.

    The result of the first HTTP call is a Mono of GetResult:

    private Mono<GetResult> getResult() {
        return WebClient
                .get()
                //...
                .retrieve()
                .bodyToMono(GetResult.class);
    }
    

    Similarly, the second HTTP call returns a Mono of PostResult:

    private Mono<PostResult> postResult(String firstResult) {
        return WebClient
                .post()
                //...
                .retrieve()
                .bodyToMono(PostResult.class);
    }
    

    Finally, combine these two publisher in order to build your pipeline using the flatmap operator:

    @PostMapping
    public Mono<Void> abbina(@RequestBody Attribute documentsUploadRequest) {
        return getResult()
                .flatMap(result -> postResult(result.getValue()))
                .then();
    }
    

    I'd recommend taking a look at the following guide: Building a Reactive RESTful Web Service