Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

like image 435
ovnia Avatar asked Sep 25 '18 05:09

ovnia


2 Answers

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 .

like image 167
Armen Arzumanyan Avatar answered Oct 12 '22 23:10

Armen Arzumanyan


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.

like image 22
jannis Avatar answered Oct 13 '22 00:10

jannis