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?
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);});
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.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With