当前位置: 首页 > 知识库问答 >
问题:

抛出异常作为反应流验证的最合适方法

牟飞沉
2023-03-14

我有一个反应流,我希望其中的一个步骤是应用验证检查,如果失败,将抛出一个异常。有没有一种普遍接受的风格可以做到这一点?据我所知,我有三个选项(使用Mono),分别位于then(),,filter()和map()。

  1. filter()最接近我想要的流,因为我实际上并没有尝试更改流中的数据类型或切换到另一个流。但是,过滤器应该返回true/false以过滤掉项目,所以总是返回TRUE有点傻。
  2. 那么()允许我专门选择错误/成功排放,但有时对于这种类型的验证,我无法轻松地将其拆分为自己的私有方法,并且样板使流声明更混乱。
  3. map()与使用filter()几乎相同,只是您总是在输入中返回而不是TRUE。

作为一个非常人为的例子,考虑一个服务,它有一个0封或更多封信要发送给一个人的列表:

public interface Person {
    UUID getId();
    List<String> getKnownLanguages();
}

public interface Letter {
    String getLanguage();
}

public class LetterService {
    private Letter findOneLetterForPerson(final UUID id) { /* ... */ }

    private void removeLetter(final Letter letter) { /* ... */ }
}

创建如下方法的更好选项是什么:

public Mono<Optional<Letter>> getNextValidLetterForPerson(final Person person) {
    return Mono.just(person)
               .and(this::getNextLetterForPerson)

               /////////////////////////////////////////
               //
               .filter(this::validatePersonCanReadLetter1)
               .map(Tuple2::getT2)
               //
               // OR
               //
               .then(this::validatePersonCanReadLetter2)
               //
               // OR
               //
               .map(this::validatePersonCanReadLetter3)
               //
               /////////////////////////////////////////

               // If the letter was invalid for the person, remove the letter from the
               // the system as a side effect, and retry retrieving a letter to send
               .doOnError(this::removeInvalidLetter)
               .retry(this::ifLetterValidationFailed)

               // Map the result to an appropriate Optional
               .map(Optional::of)
               .defaultIfEmpty(Optional.empty());
}

上述示例中使用的支持方法有:

public static class LetterInvalidException extends RuntimeException {
    private Letter mLetter;
    public LetterInvalidException(final Letter letter) { mLetter = letter; }
    public Letter getLetter() { return mLetter; }
}


/** Gets the next letter for a person, as a reactive stream */
private Mono<Letter> getNextLetterForPerson(final Person person) {
    return Mono.create(emitter -> {
        final Letter letter = mLetterService.findOneLetterForPerson(person.getId());

        if (letter != null) {
            emitter.success(letter);
        }
        else {
            emitter.success();
        }
    });
}

/** Used to check whether the cause of an error was due to an invalid letter */
private boolean ifLetterValidationFailed(final Throwable e) {
    return e instanceof LetterInvalidException;
}

/** Used to remove an invalid letter from the system */
private void removeInvalidLetter(final Throwable e) {
    if (ifLetterValidationFailed(e)) {
        mLetterService.removeLetter(((LetterInvalidException)e).getLetter());
    }
}

/*************************************************************************
 *
 *************************************************************************/

private boolean validatePersonCanReadLetter1(final Tuple2<Person, Letter> tuple) {
    final Person person = tuple.getT1();
    final Letter letter = tuple.getT2();

    if (!person.getKnownLanguages().contains(letter.getLanguage())) {
        throw new LetterInvalidException(letter);
    }

    return true;
}

private Mono<Letter> validatePersonCanReadLetter2(final Tuple2<Person, Letter> tuple) {
    return Mono.create(emitter -> {
        final Person person = tuple.getT1();
        final Letter letter = tuple.getT2();

        if (!person.getKnownLanguages().contains(letter.getLanguage())) {
            emitter.error(new LetterInvalidException(letter));
        }
        else {
            emitter.success(letter);
        }

    });
}

private Letter validatePersonCanReadLetter3(final Tuple2<Person, Letter> tuple) {
    final Person person = tuple.getT1();
    final Letter letter = tuple.getT2();

    if (!person.getKnownLanguages().contains(letter.getLanguage())) {
        throw new LetterInvalidException(letter);
    }

    return letter;
}

理想情况下,我喜欢诸如Mono之类的方法


共有1个答案

苗信鸥
2023-03-14

也许handle是一个更好的解决方案,它可以作为映射和过滤器的组合:

Mono.just(p).and(test::getNextLetterForPerson).handle((tuple, sink) -> {
    final Person person = tuple.getT1();
    final Letter letter = tuple.getT2();

    if (!person.getKnownLanguages().contains(letter.getLanguage())) {
        sink.error(new LetterInvalidException(letter));
        return;
    }

    sink.next(letter);
}).subscribe(value -> System.out.println(((Letter) value).getLanguage()),
t -> System.out.println(t.getMessage()));

正如您所看到的,它几乎就像您的validatePersonCanReadLetter3

 类似资料:
  • 在测试中,我使用的是mockobject: 我试图验证其方法的使用: 但它抛出以下异常: 组织。莫基托。例外情况。滥用。UnfinishedVerificationException:此处缺少验证(模拟)的方法调用: 这一行的例外点是: setMaxRows接受一个int。 当我注释掉这一行时,测试成功了。调试程序时,我可以看到正在设置的setMaxRows方法: BrandLabels是一个Li

  • 我正在使用RabbitMQ的spring cloud stream。我试图通过在运行时删除队列并将消息发送到删除的队列来进行否定测试。 我正在侦听来自队列1的消息,并将消息发送到队列2(已删除的一个)。我原以为上面的代码会抛出异常,但事实并非如此。甚至从队列1读取的消息也已被确认。我在队列1和队列2上有一个死信队列,但消息没有进入DLQ。

  • 问题内容: 除了抛出,这意味着我方法的所有调用者都需要捕获Exception(可以包含RuntimeExceptions),我想在出现问题时抛出一种更特定类型的异常。 我可以创建自己的扩展了Exception或其他异常类型的异常类型,但是我很好奇是否可以重用Java语言所附带的一些异常,例如: IllegalArgumentException UnsupportedOperationExcepti

  • 抛出异常的行为是否可能抛出不同的异常? 为了抛出异常,必须(可选地)分配新对象,并调用其构造函数(隐式调用fillinstacktrace)。在某些情况下,听起来像addSupressed也被称为。那么如果没有足够的内存会发生什么呢?JVM是否需要预分配内置异常?例如,(1/0)会抛出OutOfMemoryError而不是ArithmeticException吗? 此外,构造函数是一个方法调用,因

  • 我本可以使用而不是第二个,因为它不映射任何东西,但我不确定这是否是peek方法的可接受用法。 我也可以在第二个中使用一个有状态映射器来只运行一次,而不是用索引压缩,我想这是可以接受的,因为我已经使用了一个有状态谓词...

  • 上一节展示了如何为ListOfNumbers类中的writeList方法编写异常处理程序。 有时,它适合代码捕获可能发生在其中的异常。 但在其他情况下,最好让一个方法进一步推给上层来调用堆栈处理异常。 例如,如果您将ListOfNumbers类提供为类包的一部分,则可能无法预期包的所有用户的需求。 在这种情况下,最好不要捕获异常,并允许一个方法进一步推给上层来调用堆栈来处理它。 如果writeLi