ovnia
ovnia

Reputation: 2500

How to combine Flux and ResponseEntity in Spring Webflux controllers

I use Monos with ResponseEntitys in my Webflux controllers in order to manipulate headers and other response info. For example:

@GetMapping("/{userId}")
fun getOneUser(@PathVariable userId: UserId): Mono<ResponseEntity<UserDto>> {
    return repository.findById(userId)
        .map(User::asDto)
        .map { ResponseEntity.ok(it) }
        .defaultIfEmpty(ResponseEntity.notFound().build())
}

@GetMapping
fun getAllUsers(): Flux<UserDto> {
    return repository.findAllActive().map(User::asDto)
}

both works fine but there are cases where it is required to have ResponseEntity in conjunction with Flux as well. What should the response type be? Is it correct to use ResponseEntity<Flux<T>>?

For example:

@GetMapping("/{userId}/options")
fun getAllUserOptions(@PathVariable userId: UserId): ??? {
    return repository.findById(userId)
        .flatMapIterable{ it.options }
        .map { OptionDto.from(it) }
        // if findById -> empty Mono then:
        //   return ResponseEntity.notFound().build() ?
        // else:
        //   return the result of `.map { OptionDto.from(it) }` ?
}

The behaviour I'd like to achieve here is that getAllUserOptions returns 404 if repository.findById(userId) is an empty Mono, otherwise return user.options as Flux.

Update: repository here is ReactiveCrudRepository

Upvotes: 16

Views: 27776

Answers (3)

andreshincapier
andreshincapier

Reputation: 101

works for me, let me know if you have a trouble

@PostMapping(value = "/bizagi/sendmsg")
public Mono<ResponseEntity<?>> sendMessageToQueue(@RequestBody BizagiPost bizagiPost) {
    Mono<BodyReturn> retorno = useCase.saveMsg(bizagiPost);
    Map<String, Object> response = new HashMap<>();
    return retorno.map(t ->
    {
        if (t.getStatusCode().equals("200")) {
            response.put("message", t.getReazon());
            return new ResponseEntity(t, HttpStatus.OK);
        } else {
            response.put("message", t.getReazon());
            return new ResponseEntity(t, HttpStatus.BAD_REQUEST);
        }
    });
}

Upvotes: -1

Armen Arzumanyan
Armen Arzumanyan

Reputation: 2063

You can use by returning Mono with ResponseEntity

like this

public Mono<ResponseEntity<?>> oneRawImage(
        @PathVariable String filename) {
        // tag::try-catch[]
        return imageService.findOneImage(filename)
            .map(resource -> {
                try {
                    return ResponseEntity.ok()
                        .contentLength(resource.contentLength())
                        .body(new InputStreamResource(
                            resource.getInputStream()));
                } catch (IOException e) {
                    return ResponseEntity.badRequest()
                        .body("Couldn't find " + filename +
                            " => " + e.getMessage());
                }
            });         
    }

I have also example like this

  public ResponseEntity<Mono<?>>  newLive(@Valid @RequestBody Life life) {
        Mono<Life> savedLive = liveRepository.save(life);
        if (savedLive != null) {
            return new ResponseEntity<>(savedLive, HttpStatus.CREATED);
        }
        return new ResponseEntity<>(Mono.just(new Life()), HttpStatus.I_AM_A_TEAPOT);
    }

I dislike functional programming in the REST controllers.

Here is an example ReactiveController .

Upvotes: 4

jannis
jannis

Reputation: 5230

Use switchIfEmpty to throw an exception in case the user doesn't exist:

return repository
    .findById(userId)
    .switchIfEmpty(Mono.error(NotFoundException("User not found")))
    .flatMapIterable{ it.options }
    .map { OptionDto.from(it) }

Then with an exception handler translate it to a 404 response.

Upvotes: 6

Related Questions