Reputation: 789
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?
Upvotes: 5
Views: 9466
Reputation: 59221
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);});
Upvotes: 11
Reputation: 218
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.
Upvotes: 4