Problem: Returning Flux of type ResponseEntity

I have the following fragment where I want to return a Flux from a ResponseEntity<Response>:

@GetMapping("/{id}")
public Mono<ResponseEntity<Response>> findByDocumentClient(@PathVariable("id") String document){
    return Mono.just(new ResponseEntity<>(new Response(technomechanicalService.findByDocumentClient(document), HttpStatus.OK.value(), null), 
                            HttpStatus.OK))
            .onErrorResume(error -> {
                return Mono.just(new ResponseEntity<>(new Response(null, HttpStatus.BAD_REQUEST.value(), error.getMessage()), 
                                        HttpStatus.BAD_REQUEST));
            });
}

The Response object is as follows:

public class Response{
    private Object body;
    private Integer status;
    private String descStatus;

    public Response(Object body, Integer status, String descStatus) {
        this.body = body;
        this.status = status;
        this.descStatus = descStatus;
    }
}

When consuming the Get method from postman, the service responds to the following:

{
    "body": {
        "scanAvailable": true,
        "prefetch": -1
    },
    "status": 200,
    "descStatus": null
}

Why does it generate this response? Why is the list of objects not responding?

Upvotes: 0

Views: 4062

Answers (2)

Nipuna Saranga
Nipuna Saranga

Reputation: 1200

Agree with @Toerktumlare's answer. Quite comprehensive.

@Juan David Báez Ramos based on your answer(better if it were a comment), seems like what you want is putting technomechanicalService.findByDocumentClient(document) result as body in a Response object.

If so you can use Flux API's collectList() operator.

Example code:

@GetMapping("/{id}")
public Mono<ResponseEntity<Response>> findByDocumentClient(@PathVariable("id") String document) {
    return technomechanicalService.findByDocumentClient(document)
        .collectList()
        .map(
            listOfDocuments -> {
              return new ResponseEntity<>(
                  new Response(listOfDocuments, HttpStatus.OK.value(), null), HttpStatus.OK);
            }
        )
        .onErrorResume(
            error -> {
              return Mono.just(new ResponseEntity<>(
                  new Response(null, HttpStatus.BAD_REQUEST.value(), error.getMessage()),
                  HttpStatus.BAD_REQUEST));
            }
        );
}

Upvotes: 0

Toerktumlare
Toerktumlare

Reputation: 14732

It's because you are trying to code imperatively (traditional java) and you are serializing a Mono and not the actually value returned from the database. You should be coding functionally as reactor/webflux uses this type of development.

A Mono<T> is a producer that produces elements when someone subscribes to it. The subscriber is the one that started the call, in this case the client/browser.

Thats why you need to return a Mono<ResponseEntity> becuase when the client subscribes it will emit a ResponseEntity

So lets Look at your code:

@GetMapping("/{id}")
public Mono<ResponseEntity<Response>> findByDocumentClient(@PathVariable("id") String document){
    return Mono.just(new ResponseEntity<>(new Response(technomechanicalService.findByDocumentClient(document), HttpStatus.OK.value(), null), 
                            HttpStatus.OK))
            .onErrorResume(error -> {
                return Mono.just(new ResponseEntity<>(new Response(null, HttpStatus.BAD_REQUEST.value(), error.getMessage()), 
                                        HttpStatus.BAD_REQUEST));
            });
}

The first thing you do, is to put your response straight into a Mono using Mono#just. In webflux a Mono is something that can emit something and as soon as you put something in one you are also telling the server that it can freely change which thread that performs the execution. So we basically want to go into a Mono as quick as possible so we can leverage webflux thread agnostic abilities.

then this line:

technomechanicalService.findByDocumentClient(document)

returns a Mono<T> and you place that in your your Response body. So it tries to serialize that into json, while you think that it takes its internal Value and serializes that its actually serializing the Mono.

So lets rewrite your code *im leaving out the error handling for now since im writing this on mobile:

@GetMapping("/{id}")
public Mono<ServerResponse> findByDocumentClient(@PathVariable("id") String document){
    // We place our path variable in a mono so we can leverage 
    // webflux thread agnostic abilities
    return Mono.just(document)
               // We access the value by flatMapping and do our call to 
               // the database which will return a Mono<T>
               .flatMap(doc -> technomechanicalService.findByDocumentClient(doc)
                   // We flatmap again over the db response to a ServerResponse
                   // with the db value as the body
                   .flatMap(value -> ServerResponse.ok().body(value)));
}

All this is super basic reactor/webflux stuff. I assume this is your first time using webflux. And if so i highly recommend going through the Reactor getting started of how the basics work because otherwise you will have a very hard time with reactor, and later on understanding webflux.

Upvotes: 1

Related Questions