如何处理Spring Reactor Mono或Flux中的错误?

How to handle errors in Spring reactor Mono or Flux?

我在下面的代码中调整了Mono

1
2
3
4
5
6
7
8
9
10
11
12
13
14
try {
    return userRepository.findById(id)  // step 1
        .flatMap(user -> barRepository.findByUserId( user.getId())  // step 2
        .map(bar-> Foo.builder().msg("Already exists").build())  // step 3
            .switchIfEmpty(barRepository.save(Bar.builder().userId(user.getId()).build())  // step 4
                .map(bar-> Foo.builder().msg("Created").build())   // step 5
            ))
            .doOnError(throwable -> Mono.just(handleError(throwable)));
    } catch(Exception e) {

        log.error("from catch block");
        return Mono.just(handleError(e));

    }

如果在第1步中发生错误(例如,指定的ID不存在用户),是否会被doOnError或try catch块捕获,或者这两个都不捕获?

同一问题,是否在步骤2,步骤3,步骤4中发生错误。

什么是正确的代码,以便始终由doOnError捕获错误并消除try catch?

我正在使用
public interface UserRepository extends ReactiveMongoRepository<User, String>与barRepository相同。

handleError(throwable)仅执行log.error(e.getMessage()并重新调整Foo。


我认为第一个错误出现在标题中:"单声道或助焊剂"与错误处理无关。

  • 单声道最多只能发射一项(流化一个元素)
  • 助焊剂会散发出更复杂的东西(即清单)

要处理错误,您可以按照以下示例进行操作:

1
2
3
4
5
6
7
return webClient.get()
                .uri(url)
                .retrieve()
                .bodyToMono(ModelYouAreRetrieving.class)
                .doOnError(throwable -> logger.error("Failed for some reason", throwable))
                .onErrorReturn(new ModelYouAreRetrieving(...))
                .block();


@Gianluca Pinto的最后一行代码也不正确。该代码不会被编译。 onErrorReturn不适合复杂的错误处理。您应该使用的是onErrorResume。

请参阅:https://grokonez.com/reactive-programming/reactor/reactor-handle-error#21_By_falling_back_to_another_Flux

onErrorResume将回退到另一个Flux,并让您捕获和管理先前Flux引发的异常。如果查看onErrorReturn的实现,您会发现onErrorReturn实际上是在使用onErrorResume。

所以这里的代码应该是:

1
.onErrorResume(throwable -> Mono.just(handleError(throwable)));

DoOnError仅会产生副作用,并且如果findById失败,则它会返回Mono.Error(),这应该是可行的。

1
2
3
4
5
6
7
8
9
return userRepository.findById(id)
    .flatMap ( user ->
        barRepository.findByUserId(user.getId())
        .map((user,bar)-> Foo.builder().msg("Already exists").build())  
        .switchIfEmpty(barRepository.save(Bar.builder().userId(user.getId()).build())
        .map(bar-> Foo.builder().msg("Created").build())

    ))
    .onErrorReturn(throwable -> Mono.just(handleError(throwable)));

仅当您调用链的阻塞操作,或者在进入反应式链之前发生运行时错误时,try catch才起作用。 doOn操作不会修改链,它们仅用于副作用。由于flatMap需要生产者,因此您需要从调用中返回Mono,在这种情况下,如果发生错误,则它将传播错误。除非另外处理,否则所有反应链中的错误都会传播。


使用Exceptions.propagate(e),它将已检查的异常包装为特殊的运行时异常,可由onError

处理

Below Code tries to covers User attributes in upper case. Now, when it encounters kyle the checked exception is throws and MIKE is returned from onErrorReturn

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
@Test
void Test19() {
    Flux.fromIterable(Arrays.asList(new User("jhon","10000"),
            new User("kyle","bot")))
        .map(x -> {
            try {
                return toUpper(x);
            } catch (TestException e) {
                throw Exceptions.propagate(e);
            }
        })
        .onErrorReturn(new User("MIKE","BOT")).subscribe(x -> System.out.println(x));
}

protected final class TestException extends Exception {
    private static final long serialVersionUID = -831485594512095557L;
}

private User toUpper(User user) throws TestException{
    if (user.getName().equals("kyle")) {
        throw new TestException();
    }
    return new User(user.getName().toUpperCase(), user.getProfession().toUpperCase());
}

输出

1
2
User [name=JHON, profession=10000]
User [name=MIKE, profession=BOT]

@James Ralston的代码的最后一行是错误的。正确的代码应为:

1
2
3
4
5
6
7
8
9
return userRepository.findById(id)
.flatMap ( user ->
    barRepository.findByUserId(user.getId())
    .map((user,bar)-> Foo.builder().msg("Already exists").build())  
    .switchIfEmpty(barRepository.save(Bar.builder().userId(user.getId()).build())
    .map(bar-> Foo.builder().msg("Created").build())

))
.onErrorReturn(Mono.just(handleError(throwable)));