Issue
I am currently working on a Spring Boot application wrapping an external REST API into my own GraphQL API.
So far, I've did this method returning a SerieDetails
:
public SerieDetails findOrThrow(long id) throws RuntimeException {
try {
return this.webClient
.get()
.uri("/tv/{id}?api_key={apiKey}&language={lang}", id, this.apiKey, this.lang)
.retrieve()
.bodyToMono(SerieDetails.class)
.log()
.block();
} catch(Exception e ) {
throw new RuntimeException("Can't reach the API");
}
}
Turning this JSON into my own object :
{
"id": 71912,
"name": "The Witcher",
"overview": "Geralt of Rivia, a mutated monster-hunter for hire, journeys toward his destiny in a turbulent world where people often prove more wicked than beasts."
"last_air_date": "2019-12-20",
...
}
Which works great. But now I am doing another call returning a list of SerieDetails
with a given query and it returns the result under the result
array :
{
"page": 1,
"results": [
{
"id": 71912,
"name": "The Witcher",
"overview": "Geralt of Rivia, a mutated monster-hunter for hire, journeys toward his destiny in a turbulent world where people often prove more wicked than beasts."
"first_air_date": "2019-12-20",
...
},
{
"id": 106541,
"name": "The Witcher: Blood Origin",
"overview": "Set in an elven world 1200 years before the world of The Witcher, the show charts the origins of the very first Witcher, and the events that lead to the pivotal “conjunction of the spheres,” when the worlds of monsters, men, and elves merged to become one.",
"first_air_date": "2002-09-22",
...
}
]
}
And I don't know how to get to it with WebClient. I tried this so far :
public List<SerieDetails> findByQuery(String query) {
return this.webClient
.get()
.uri("/search/tv?api_key={apiKey}&language={lang}&query={query}", this.apiKey, this.lang, query)
.retrieve()
.bodyToFlux(SerieDetails.class)
.log()
.collect(Collectors.toList())
.block();
}
But it doesn't work.
Solution
You need to change the object to which the JSON is deserialized. The functions bodyToMono
and bodyToFlux
create two different streams, where Mono
and Flux
are the publishers:
Mono
emits at most one item;Flux
emits a sequence from zero to N items.
The items are the instances of the specified class. In your case, it is the JSON response that is converted to SerieDetails
.
Multiple items do not mean an array of SerieDetails
, but multiple JSON responses with the structure of SerieDetails
. You can imagine this like different object instances of the same class.
The simpler solution would be creating the following class:
class PaginatedSerieDetails {
private int page;
private List<SerieDetails> results;
// getters and setters
}
Then you would use this one when you do the call as follows:
public List<SerieDetails> findByQuery(String query) {
return this.webClient
.get()
.uri("/search/tv?api_key={apiKey}&language={lang}&query={query}", this.apiKey, this.lang, query)
.retrieve()
.bodyToMono(PaginatedSerieDetails.class)
.map(PaginatedSerieDetails::getResults())
.log()
.block();
}
As a side note, please strive to avoid .block()
while using WebFlux. When you do it you defeat the whole purpose of using the reactive stack because you are saying you want to actually make the thread wait for the Mono or Flux to emit a value.
Answered By - João Dias