I'm new to Spring Reactive Web, and i met the following issue. I want to create a microservice A with an endpoint which accepts a number N, sends N requests to microservice B (which returns a string for each request), wraps the strings into objects, combines them into a List/Flux (?) and returns a JSON with those objects, like:
{
"number": 4,
"objects": [
{
"name": "first"
},
{
"name": "second"
},
{
"name": "third"
},
{
"name": "fourth"
}
]
}
I want to use a functional endpoint for this. So i tried the following (made my best to simplify it):
public class MyObject {
private String name; // here should be a value received from B
// ...
}
public class MyResponse {
private int number;
private Flux<MyObject> objects; // or List?
// ...
}
@Component
@RequiredArgsConstructor
public class MyHandler {
private final MyClient client;
public Mono<ServerResponse> generate(ServerRequest serverRequest) {
return serverRequest.bodyToMono(MyRequestBody.class)
.flatMap(request -> buildServerResponse(HttpStatus.OK, buildResponseBody(request)));
}
private Mono<ServerResponse> buildServerResponse(HttpStatus status, Mono<MyResponse> responseBody) {
return ServerResponse.status(status)
.contentType(MediaType.APPLICATION_JSON)
.body(responseBody, MyResponse.class);
}
private Mono<MyResponse> buildResponseBody(MyRequestBody request) {
return Mono.just(MyResponse.builder()
.number(request.getNumber())
.objects(getObjects(request.getNumber())
.build());
}
private Flux<MyObject> getObjects(int n) {
// how to receive n strings from MyClient, make MyObject from each of them and then combine them together to a Flux/List?
}
public class MyClient {
public Mono<String> getName() {
WebClient client = WebClient.builder().baseUrl(getUrl()).build();
return client.get()
// ...
.retrieve()
.bodyToMono(String.class);
}
private String getUrl() {
// ...
}
}
So, if i use Flux in MyResponse, i receive a response like:
{
"number": 4,
"objects": {
"prefetch": 2147483647,
"scanAvailable": true
}
}
on the other hand, if i try to use a List, it seems to require blocking at some point, and i receive errors related to it. So, how do i do it?
Thanks in advance!
UPDATE: if i use collectList().block()
to make a List out of Flux, i receive this:
java.lang.IllegalStateException: block()/blockFirst()/blockLast() are blocking, which is not supported in thread <...>
As I understand from answers to this question, i should never block when my method returns Mono
/ Flux
. Exctracting the block()
call to a separate method which is called from the one returning Mono
/ Flux
doesn't help. If i use share()
before block()
, then my request just executes forever, for some reason which i don't understand yet.
Alright, i made it.
Flux
as a field doesn't work in a desired way, so i need a List
.
public class MyResponse {
private int number;
private List<MyObject> objects;
// ...
}
Now i need a way to make a List<MyObject>
out of multiple Mono<String>
s where each MyObject
has a String
field.
The thing is that we don't ever get rid out of Mono
or Flux
, so we go for Flux<MyObject>
first.
private Flux<MyObject> getObjects(int n) {
return Flux.range(0, n) // Flux<Integer>
.map(i -> myClient.getName()) // Flux<String>
.map(name -> new MyObject(name)); // Flux<MyObject>
}
and then we make Flux:
private Mono<MyResponse> buildResponseBody(MyRequestBody request) {
return getObjects(request.getNumber()) // Flux<MyObject>
.collectList() // Mono<List<MyObject>>
.map(objects -> MyResponse.builder() // Mono<MyResponse>
.number(request.getNumber())
.objects(objects)
.build()));
}
This way it works, as we don't have to block anything.
The problem only appears when we want to get rid of Mono
/ Flux
at some point, like if we want a pure List<MyObject>
. But as long as we have a Mono
and/or Flux
as both input and output, we can do all the manipulations with the methods of these classes, preserving Mono
or Flux
at each stage.
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.