21

I'm having the following simplified handler function (Spring WebFlux and the functional API using Kotlin). However, I need a hint how to detect an empty Flux and then use noContent() for 404, when the Flux is empty.

fun findByLastname(request: ServerRequest): Mono<ServerResponse> {
    val lastnameOpt = request.queryParam("lastname")
    val customerFlux = if (lastnameOpt.isPresent) {
        service.findByLastname(lastnameOpt.get())
    } else {
        service.findAll()
    }
    // How can I detect an empty Flux and then invoke noContent() ?
    return ok().body(customerFlux, Customer::class.java)
}
vmaldosan
  • 444
  • 4
  • 14
Juergen Zimmermann
  • 2,084
  • 7
  • 29
  • 43

4 Answers4

31

From a Mono:

return customerMono
           .flatMap(c -> ok().body(BodyInserters.fromObject(c)))
           .switchIfEmpty(notFound().build());

From a Flux:

return customerFlux
           .collectList()
           .flatMap(l -> {
               if(l.isEmpty()) {
                 return notFound().build();

               }
               else {
                 return ok().body(BodyInserters.fromObject(l)));
               }
           });

Note that collectList buffers data in memory, so this might not be the best choice for big lists. There might be a better way to solve this.

Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
  • the modified variant doesn't work either. `customerFlux.collectList()` is resulting in a Mono which contains an empty list when the originating Flux is empty. Therefore, the Mono is not empty (because it contains an empty list) and ok() is invoked. – Juergen Zimmermann Aug 28 '17 at 12:14
  • 1
    Solution found. Inside `flatMap()` an if-statement is required: `if (l.isEmpty()) notFound()... else ok()...` Then `switchIfEmpty(...)` is obsolete here. – Juergen Zimmermann Aug 28 '17 at 12:24
  • Is this still the best way to return a 404 on an empty `Flux`? – Gavin Jan 29 '21 at 10:49
13

Use Flux.hasElements() : Mono<Boolean> function:

return customerFlux.hasElements()
                   .flatMap {
                     if (it) ok().body(customerFlux)
                     else noContent().build()
                   }
RJ.Hwang
  • 1,683
  • 14
  • 24
10

In addition to the solution of Brian, if you are not want to do an empty check of the list all the time, you could create a extension function:

fun <R> Flux<R>.collectListOrEmpty(): Mono<List<R>> = this.collectList().flatMap {
        val result = if (it.isEmpty()) {
            Mono.empty()
        } else {
            Mono.just(it)
        }
        result
    }

And call it like you do it for the Mono:

return customerFlux().collectListOrEmpty()
                     .switchIfEmpty(notFound().build())
                     .flatMap(c -> ok().body(BodyInserters.fromObject(c)))
zennon
  • 1,080
  • 10
  • 25
6

I'm not sure why no one is talking about using the hasElements() function of Flux.java which would return a Mono.

ayush prashar
  • 436
  • 5
  • 13