spring 如何检查 Mono 是否为空?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/47242335/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-09-08 01:32:07  来源:igfitidea点击:

How to check if Mono is empty?

springkotlinproject-reactorreactive

提问by voliveira89

I'm developing a app with Spring Boot 2.0 and Kotlin using the WebFlux framework.

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

I want to check if a user id exits before save a transaction. I'm stucked in a simple thing like validate if a Mono is empty.

我想在保存交易之前检查用户 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()) }
}

It is possible to do what I want?

有可能做我想做的事吗?

回答by Oleh Dokuka

The techniques that allow checking whether Flux/Monois empty

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

Using operators .switchIfEmpty/.defaultIfEmpty/Mono.repeatWhenEmpty

使用运营商.switchIfEmpty/ .defaultIfEmpty/Mono.repeatWhenEmpty

Using mentioned operators you will be able to react to the case when Stream has been completed without emitting any elements.

使用上述运算符,您将能够对 Stream 已完成而不发出任何元素的情况做出反应。

First of all, remember that operators such .map, .flatMap, .filterand many others will not be invoked at all if there no onNexthas been invoked. That means that in your case next code

首先,请记住,等运营商.map.flatMap.filter和许多其他人将不存在任何根本不会被调用onNext已被调用。这意味着在你的情况下下一个代码

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()) }

will not be invoked at all, if transactionwill be empty.

根本不会被调用,如果transaction将为空

In case if there is a requirement for handling cases when your flow is empty, you should consider operators like next in the following manner:

如果您的流程为空时需要处理案例,您应该按照以下方式考虑像 next 这样的操作符:

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

Actual solution

实际解决方案

Also, I have noted that you created two sub-flows from the main transaction. Actually, following code will not be executed at all:

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

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

and will be only executed the last one, which is returned from the method. That happens because you ain't subscribed using operator .subscribe(...).

并且只会执行从方法返回的最后一个。发生这种情况是因为您没有使用 operator 订阅.subscribe(...)

The second point, you can't subscribe to the same request body more the one time (kind of limitation for WebClient's reponse). Thus you are required to share your request body in the next way, so completed example will be:

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

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) })
}

Or more simple case without sharing transaction flow but using Tuple:

或者更简单的情况,不共享交易流程但使用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)
                }
            }
}

回答by Arno

Let me start by saying I am a newbie on reactive (java) and on this forum. I think you cannot really check in this code if a mono is empty because a mono represents code that will be executed later on, so in this code body you won't know yet if its is empty. Does that make sense?

首先让我说我是反应式(java)和这个论坛的新手。我认为如果单声道为空,您就无法真正检查此代码,因为单声道表示稍后将执行的代码,因此在此代码主体中,您尚不知道它是否为空。那有意义吗?

I just wrote something similar in Java which seems to work (but not 100% this is the best approach either):

我刚刚在 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);
}

回答by Anil

We can use switchIfEmpty method for this

我们可以为此使用 switchIfEmpty 方法

Below example, I'm checking if the user exists with email if not then add it

下面的示例,我正在检查用户是否存在电子邮件,如果不存在,则添加它

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));