简体   繁体   中英

Can I have Flux as a field of ServerResponse body?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM