我有一个反应流,我想要一步应用验证检查,如果失败,将抛出异常。是否有一种普遍接受的风格?据我所知,我在Mono
,then()
和filter()
中有三个选项(使用map()
)。
filter()
最接近我想要的流程,因为我实际上并没有尝试更改流中的数据类型或切换到另一个流。但是,filter应该返回true / false来过滤掉项目,所以总是返回TRUE有点傻。then()
让我专门选择错误/成功排放,但有时候对于这种类型的验证,我无法轻易地将其拆分为自己的私有方法,并且样板文件使流声明更容易阅读。 / LI>
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<T> validate(..)
这样的方法,它允许测试流项并返回或抛出异常(如果返回,框架会将其视为错误),但我相当新反应式编程并没有看到任何类似的工作。
答案 0 :(得分:3)
也许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