Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Returning multiple Monos in Spring Webflux

I'm trying to play about with SpringBoot 2.0 and the new reactive webFlux library. I want to know how I can return the results of two calls made via the none blocking WebClient to the caller of my Springboot API. The code I have is:

@RequestMapping("/search")
public CombinedResults perfomSearch(@RequestParam final String searchTerm) {
    Mono<SearchResponse> fasMono = searchService.getSearchResults(searchTerm, "fh");
    Mono<SearchResponse> esMono = searchService.getSearchResults(searchTerm, "es");
    CombinedResults combinedResults = new CombinedResults(fasMono, esMono);
    return combinedResults;

}

The CombinedResult object is just a POJO:

public class CombinedResults {

private Mono<SearchResponse> fasSearchResponse;

private Mono<SearchResponse> esSearchResponse;

public CombinedResults(final Mono<SearchResponse> fasSearchResponse, final Mono<SearchResponse> esSearchResponse) {
    this.fasSearchResponse = fasSearchResponse;
    this.esSearchResponse = esSearchResponse;
}

public Mono<SearchResponse> getFasSearchResponse() {
    return fasSearchResponse;
}

public void setFasSearchResponse(final Mono<SearchResponse> fasSearchResponse) {
    this.fasSearchResponse = fasSearchResponse;
}

public Mono<SearchResponse> getEsSearchResponse() {
    return esSearchResponse;
}

public void setEsSearchResponse(final Mono<SearchResponse> esSearchResponse) {
    this.esSearchResponse = esSearchResponse;
}

However if I call this the response I get back is

{
  "fasSearchResponse": {
    "scanAvailable": true
  },
  "esSearchResponse": {
    "scanAvailable": true
  }
}

Rather than the contents of the SearchResponse objects. I feel I might be missing a fundamental point of how this is supposed to work! My thought was that because the WebClient is none blocking I can fire of the two calls to the web services and then combine them without the need for completable futures etc?

like image 707
Sutty1000 Avatar asked Dec 11 '22 07:12

Sutty1000


2 Answers

Spring WebFlux doesn't support nested reactive types. You should instead have something like this:

Mono<SearchResponse> fasMono = searchService.getSearchResults(searchTerm, "fh");
Mono<SearchResponse> esMono = searchService.getSearchResults(searchTerm, "es");
Mono<CombinedResults> results = fasMono.zipWith(esMono, 
    (fas, es) -> {return new CombinedResults(fas, es);});
like image 175
Brian Clozel Avatar answered Jan 05 '23 14:01

Brian Clozel


I think that you should return a Mono of an object that represents the model responded by this action. Suppose that CombinedResults is your model. This class should be something like:

public class CombinedResults {

    private SearchResponse fasSearchResponse;

    private SearchResponse esSearchResponse;

    public CombinedResults(final SearchResponse fasSearchResponse, final SearchResponse esSearchResponse) {
        this.fasSearchResponse = fasSearchResponse;
        this.esSearchResponse = esSearchResponse;
    }

    //... getters AND/OR setters
}

And, on your controller you do something like this:

@RequestMapping("/search")
public Mono<CombinedResults> perfomSearch(@RequestParam final String searchTerm) {
    Mono<SearchResponse> fasMono = searchService.getSearchResults(searchTerm, "fh");
    Mono<SearchResponse> esMono = searchService.getSearchResults(searchTerm, "es");
    Mono<CombinedResults> combinedResults = 
        fasMono
          .flatMap(fh -> esMono.map(es -> new CombinedResults(fh, es)));
    return combinedResults;
}

In that way you are returning a Mono object holding what you wanted as a response. The chain of operations fasMono.flatMap with esMono.map builds the CombinedResults when both Mono emit items. This combination is rather common when trying to join two Monos into one. I think you could also use zip operator to join the Monos. All of this is unrelated to WebClient. If your getSearchResults performs only async-nonblocking operations than everything is async-nonblocking.

like image 33
Felipe Moraes Avatar answered Jan 05 '23 15:01

Felipe Moraes