如何在空 Mono 之后链接反应操作而不阻塞?

Adr*_*enW 1 reactive-programming project-reactor spring-webflux

基本上我想要实现的是调用第二个存储库(a ReactiveCrudRepository)或抛出异常,具体取决于调用第一个存储库的结果。

我最初的想法是这样的:

/** Reactive with blocking code */
public Flux<SecondThing> getThings(String firstThingName) {
    FirstThing firstThing = firstRepo
        .findByName(firstThingName)
        // Warning: "Inappropriate blocking method call"
        .blockOptional()  // this fails in test-context
        .orElseThrow(() -> new FirstThingNotFound(firstThingName));

    return secondRepo.findAllByFirstThingId(firstThing.getId());
}
Run Code Online (Sandbox Code Playgroud)

这对应于以下非反应式方法:

/** Non-reactive */
public List<SecondThing> getThings(String firstThingName) {
    FirstThing firstThing = firstRepo
        .findByName(firstThingName)
        .orElseThrow(() -> new FirstThingNotFound(firstThingName));

    return secondRepo.findAllByFirstThingId(firstThing.getId());
}
Run Code Online (Sandbox Code Playgroud)

我还没有找到一种方法以反应式非阻塞方式做到这一点。Mono我所需要的只是在第一次调用中出现空值时抛出错误,如果不为空则继续管道;但我在这里似乎无法正确使用onErrorStopdoOnError正确使用,并且map没有帮助,因为它跳过了空的Mono.

如果我使用id而不是,我有一个解决方法name,但我对它不太满意,因为它在 是 的实例FirstThing但没有SecondThing链接到它的情况下显示出不同的行为:

/** Reactive workaround 1 */
public Flux<SecondThing> getThings(Long firstThingId) {
    return secondRepo
        .findAllByFirstThingId(firstThingId)
        .switchIfEmpty(
            Flux.error(() -> new FirstThingNotFound(firstThingName))
        );
}
Run Code Online (Sandbox Code Playgroud)

我发现的另一个解决方法如下,它将空值替换Mononull值,但它看起来不正确并且也会引发警告:

/** Reactive workaround 2 */
public Flux<SecondThing> getThings(String firstThingName) {
    return firstRepo
        .findByName(firstThingName)
        // Warning: "Passing 'null' argument to parameter annotated as @NotNull"
        .defaultIfEmpty(null)
        .flatMapMany(
            firstThing -> secondRepo.findAllByFirstThingId(firstThing.getId()
        )
        .onErrorMap(
            NullPointerException.class, e -> new FirstThingNotFound(firstThingName)
        );
}
Run Code Online (Sandbox Code Playgroud)

将调用链接到两个存储库以便存在或不存在对第二个存储库FirstThing的调用的请求条件的正确方法是什么?firstThingName

Adr*_*enW 5

我找到了一个如此简单的解决方案,我可能会为没有早点找到它而感到羞愧:

public Flux<SecondThing> getThings(String firstThingName) {
    return firstRepo
        .findByName(firstThingName)
        .switchIfEmpty(Mono.error(() -> new FirstThingNotFound(firstThingName)))
        .flatMapMany(
            firstThing -> secondRepo.findAllByFirstThingId(firstThing.getId()
        );
}
Run Code Online (Sandbox Code Playgroud)

诀窍在于,它switchIfEmpty不会强迫您选择“有效”的替换值,因此可以使用 aMono.error直接传播正确的异常。