如何检查Mono是否为空?

时间:2017-11-11 20:12:49

标签: spring kotlin project-reactor reactive

我正在使用WebFlux框架开发一个使用Spring Boot 2.0和Kotlin的应用程序。

我想在保存交易之前检查用户ID是否退出。我被困在一个简单的事情,如验证Mono是否为空。

fun createTransaction(serverRequest: ServerRequest) : Mono<ServerResponse> {
    val transaction = serverRequest.body(BodyExtractors.toMono(Transaction::class.java))

    transaction.flatMap {
        val user = userRepository.findById(it.userId)
        // If it's empty, return badRequest() 
    } 

    return transaction.flatMap { transactionRepository.save(it).then(created(URI.create("/transaction/" + it.id)).build()) }
}

可以做我想做的事吗?

3 个答案:

答案 0 :(得分:18)

允许检查Flux / Mono是否为空的技术

使用运算符.switchIfEmpty / .defaultIfEmpty / Mono.repeatWhenEmpty

使用上述操作符,您可以在Stream完成时对此案例作出反应,而不会发出任何元素。

首先,请记住,如果没有调用.map,则根本不会调用.flatMap.filteronNext等许多其他运算符。 这意味着在你的情况下下一个代码

transaction.flatMap {
    val user = userRepository.findById(it.userId)
    // If it's empty, return badRequest() 
} 

return transaction.flatMap { transactionRepository.save(it).then(created(URI.create("/transaction/" + it.id)).build()) }
如果transaction为空,则根本不会调用

如果您的流量为空时需要处理案例,您应该按以下方式考虑下一个操作符:

transaction
   .flatMap(it -> {
      val user = userRepository.findById(it.userId)
   })
   .swithIfEmpty(Flux.defer(() -> Flux.just(badRequest())));

实际解决方案

另外,我注意到您从主transaction创建了两个子流。实际上,以下代码根本不会被执行:

transaction.flatMap {
    val user = userRepository.findById(it.userId)
    // If it's empty, return badRequest() 
}  

并且只会执行从方法返回的最后一个。之所以发生这种情况,是因为您未使用运营商.subscribe(...)订阅。

第二点,您不能一次订阅同一个请求主体(WebClient的响应限制)。因此,您需要以下一种方式共享您的请求正文,因此完成的示例将是:

fun createTransaction(serverRequest: ServerRequest): Mono<ServerResponse> {
    val transaction = serverRequest.body(BodyExtractors.toMono(Transaction::class.java)).cache()

    transaction
            .flatMap { userRepository.findById(it.userId) }
            .flatMap { transaction.flatMap { transactionRepository.save(it) } }
            .flatMap { ServerResponse.created(URI.create("/transaction/" + it.id)).build() }
            .switchIfEmpty(transaction.flatMap { ServerResponse.badRequest().syncBody("missed User for transaction " + it.id) })
}

或者更简单的情况,没有共享事务流但使用Tuple

fun createTransaction(serverRequest: ServerRequest): Mono<ServerResponse> {
    val emptyUser = !User()
    val transaction = serverRequest.body<Mono<Transaction>>(BodyExtractors.toMono(Transaction::class.java))

    transaction
            .flatMap { t ->
                userRepository.findById(t.userId)
                        .map { Tuples.of(t, it) }
                        .defaultIfEmpty(Tuples.of(t, emptyUser))
            }
            .flatMap {
                if (it.t2 != emptyUser) {
                    transactionRepository.save(it.t1)
                            .flatMap { ServerResponse.created(URI.create("/transaction/" + it.id)).build() }
                } else {
                    ServerResponse.badRequest().syncBody("missed User for transaction " + it.t1.id)
                }
            }
}

答案 1 :(得分:1)

首先让我说我是反应(java)和这个论坛上的新手。 我认为你不能真正检查这段代码是否单声道是空的,因为单声道代表稍后将执行的代码,所以在这个代码体中你还不知道它是否为空。这有意义吗?

我刚刚在Java中写了类似的东西似乎有效(但不是100%这也是最好的方法):

    public Mono<ServerResponse> queryStore(ServerRequest request) { 

        Optional<String> postalCode = request.queryParam("postalCode");                            

        Mono<ServerResponse> badQuery = ServerResponse.badRequest().build();
        Mono<ServerResponse> notFound = ServerResponse.notFound().build();

        if (!postalCode.isPresent()) { return  badQuery; }

        Flux<Store> stores = this.repository
                .getNearByStores(postalCode.get(), 5);

        return ServerResponse.ok().contentType(APPLICATION_JSON)
                .body(stores, Store.class)
                .switchIfEmpty(notFound);
}

答案 2 :(得分:0)

我们可以为此使用switchIfEmpty方法

在下面的示例中,我正在检查用户是否存在电子邮件(如果不存在),然后将其添加

userRepository.findByEmail(user.getEmail())
                .switchIfEmpty(s -> {
                    user.setStatus("InActive");
                    String encodedPassword = DigestUtils.sha256Hex(user.getPassword());
                    user.setPassword(encodedPassword);
                    userRepository.save(user).subscribe();
                    s.onComplete();
                }).then(Mono.just(user));