如何仅在 Mono 为空时才执行操作,如果单声道为空,如何引发错误

2022-09-03 05:45:23

我正在尝试将一个项目转换为使用Spring WebFlux,并且在处理一些基本的业务逻辑时遇到了问题。我有一个负责检索/保存记录的存储库层和一个负责应用程序业务规则的服务层。我想做的(在服务)层是检查给定用户名的用户是否已存在。如果是这样,我想用错误来回应。如果没有,我想允许插入发生。

我在存储库层调用一个方法,该方法将按用户名查找用户,如果未找到,它将返回空的Mono。这按预期工作;但是,我已经尝试了flatMap和(defaultIfEmpty和swithIfEmpty)的各种组合,但无法对其进行编译/构建。

    public Mono<User> insertUser(User user) {
        return userRepository.findByUsername(user.username())
            .flatMap(__ -> Mono.error(new DuplicateResourceException("User already exists with username [" + user.username() + "]")))
            .switchIfEmpty(userRepository.insertUser(user));
    }

我得到的错误是 ,所以 似乎没有反映适当的类型,并且转换似乎也不起作用。Mono<Object> cannot be converted to Mono<User>swithIfEmpty


答案 1

经过额外的测试,并考虑到其他开发人员的回应,我找到了以下解决方案:

    public Mono<User> insertUser(User user) {
        return userRepository.findByUsername(user.username())
            .flatMap(__ -> Mono.error(new DuplicateResourceException("User already exists with username [" + user.username() + "]")))
            .switchIfEmpty(Mono.defer(() -> userRepository.insertUser(user)))
            .cast(User.class);
    }

正如 Thomas 所说,编译器变得混乱了。我的假设是因为 返回了一个 Mono 并带有错误,而 返回一个 Mono 和 User,所以它恢复到一个带有对象的 Mono(因此需要额外的运算符来编译它)。flatMapswitchIfEmpty.cast

另一个添加是在 .否则,总是在开火。Mono.deferswitchMapswitchIfEmpty

我仍然对其他建议/替代方案持开放态度(因为这似乎是一个相当普遍的需求/模式)。


答案 2

为什么您会收到此编译器错误是由于以下原因。

flatmap获取已完成的内容,并尝试将其转换为它可以推断的任何类型。 包含一个类型,而此类型为 。MonoMono.errorObject

一种方法是将逻辑移动到平面图中。

// This is just example code using strings instead of repos
public Mono<String> insertUser(String user) {
    return Mono.just(user)
            // Here we map/convert instead based on logic
            .flatMap(__ -> {
                if (__.isEmpty())
                    return Mono.error(new IllegalArgumentException("User already exists with username [" + user + "]"));
                return Mono.just(user);
            }).switchIfEmpty(Mono.just(user));
}

switchIfEmpty恕我直言,这不是超级好的逻辑决策。文档说明

回退到替代单声道(如果此单声道在没有数据的情况下完成)

如果我们没有得到任何东西,它更像是对其他东西的回退,所以我们可以保持数据流。

您还可以

Mono.empty().doOnNext(o -> {
        throw new IllegalArgumentException("User already exists with username [" + o + "]");
    }).switchIfEmpty(Mono.just("hello")).subscribe(System.out::println);

推荐