简体   繁体   English

在方法返回 Mono 后使用 flatMap<Void>

[英]Use flatMap after method returning Mono<Void>

I'm stuck with understanding of some aspects of Project Reactor.我对 Project Reactor 的某些方面的理解感到困惑。

I have the following list of components:我有以下组件列表:

  1. Validator of input params, returns Mono<Void> or Mono.error()输入参数的验证器,返回Mono<Void>Mono.error()
  2. Service saving data to db, returns Mono<Item>服务将数据保存到数据库,返回Mono<Item>
  3. Logger for successful actions of an user, returns Mono<Void记录用户成功操作的记录器,返回Mono<Void

A business logic is quite simple: validate params (1), save an item to db (2) and log actions (3).业务逻辑非常简单:验证参数 (1)、将项目保存到数据库 (2) 和记录操作 (3)。 The problem is validator (1) returns Mono.empty() if there are no errors with input data and Mono.error() if input params contain some errors.问题是验证器 (1) 如果输入数据没有错误,则返回Mono.empty() Mono.error()如果输入参数包含一些错误,则返回 Mono.error()。

I would like to achieve the next things:我想实现以下目标:

  1. If validator returns Mono.empty() then continue chain如果验证器返回Mono.empty()则继续链
  2. If validator returns Mono.error() then immediately stop processing and throw error which will be handled by exceptionHanlder如果验证器返回Mono.error()则立即停止处理并抛出将由exceptionHanlder处理的错误

I have tried two options:我尝试了两种选择:

First with .then(Mono<Item> item) after validation.验证后首先使用.then(Mono<Item> item) It allows me to execute saving operation after validation.它允许我在验证后执行保存操作。 Given that .then() ignores any errors, I can't rise an exception.鉴于.then()忽略任何错误,我不能引发异常。

return inputValidator.validateFields(userId, projectId)
            .then(repository.save(item))
            .onErrorMap(RepoException.class, ex -> new UnexpectedError("Failed to save item", ex))
            .subscribeOn(Schedulers.boundedElastic())
            .doOnSuccess(n -> logService.logActivity(new Activity(adminId, n))
                    .subscribe());

Second with .flatMap(Function<Mono<Void>, <Mono<? extends Item> func) after validation.第二个是.flatMap(Function<Mono<Void>, <Mono<? extends Item> func)验证后。 This approach can rise an exception from validator, but I can't execute saving operation because flatMap() doesn't trigger on empty result.这种方法可能会引发验证器异常,但我无法执行保存操作,因为flatMap()不会触发空结果。

return inputValidator.validateFields(userId, projectId)
            .flatMap(v -> repository.save(item))
            .onErrorMap(RepoException.class, ex -> new UnexpectedError("Failed to save item", ex))
            .subscribeOn(Schedulers.boundedElastic())
            .doOnSuccess(n -> logService.logActivity(new Activity(adminId, n))
                    .subscribe());

It is also important to have access for created object after saving (step 2), because I need to pass it to logger service.在保存(步骤 2)后访问创建的对象也很重要,因为我需要将它传递给记录器服务。

You can't use flatMap because there is no onNext signal - use then instead.您不能使用flatMap因为没有onNext信号 - 使用then代替。 Not sure what do you mean by "called" but there is a difference between Assembly and Subscription time in reactive.不知道“被调用”是什么意思,但反应中的装配时间和订阅时间之间存在差异。 Publisher you specified in then will not be resolved in case inputValidator.validateFields returns onError signal.如果inputValidator.validateFields返回onError信号,您在then中指定的发布者将不会被解析。

Here is a test for failed validation and as you may see subscription was not triggered这是验证失败的测试,您可能会看到未触发订阅

@Test
void saveWasNotCalledIfValidationFailed() {
    var saveMock = PublisherProbe.of(Mono.just("id"));

    var repo = mock(Repository.class);
    when(repo.save())
            .thenReturn(saveMock.mono());

    var res = validateFields()
            .then(repo.save())
            .onErrorMap(IllegalArgumentException.class,
                    ex -> new IllegalStateException("Failed to save item", ex)
            );

    StepVerifier.create(res)
            .expectError(IllegalStateException.class)
            .verify();

    saveMock.assertWasNotSubscribed();
}

private Mono<Void> validateFields() {
    return Mono.error(new IllegalArgumentException("oops"));
}

public static class Repository {
    public Mono<String> save() {
        return Mono.just("id");
    }
}

and here is a test for passed validation这是通过验证的测试

@Test
void saveIsCalledIfValidationPassed() {
    var saveMock = PublisherProbe.of(Mono.just("id"));

    var repo = mock(Repository.class);
    when(repo.save())
            .thenReturn(saveMock.mono());

    var res = validateFields()
            .then(repo.save())
            .onErrorMap(IllegalArgumentException.class,
                    ex -> new IllegalStateException("Failed to save item", ex)
            );

    StepVerifier.create(res)
            .expectNext("id")
            .verifyComplete();

    saveMock.assertWasSubscribed();
}

private Mono<Void> validateFields() {
    return Mono.empty();
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM