How to handle errors in Spring reactor Mono or Flux?

后端 未结 5 2143
难免孤独
难免孤独 2021-01-05 17:02

I have below code retuning Mono:

try {
    return userRepository.findById(id)  // step 1
        .flatMap(user -> barRepository.findByUserId( u         


        
相关标签:
5条回答
  • 2021-01-05 17:16

    I think the first error is in the title: "Mono or Flux" is not related with the error handling.

    • Mono can only emit one item at the most (streams one element)
    • Flux can emit more complex stuff (i.e. List)

    To handle errors you can follow this example:

    return webClient.get()
                    .uri(url)
                    .retrieve()
                    .bodyToMono(ModelYouAreRetrieving.class)
                    .doOnError(throwable -> logger.error("Failed for some reason", throwable))
                    .onErrorReturn(new ModelYouAreRetrieving(...))
                    .block();
    
    0 讨论(0)
  • 2021-01-05 17:24

    @Gianluca Pinto's last line of code is also incorrect. The code won't be compiled. onErrorReturn is not suitable for complicated error handling. What you should use is onErrorResume.

    see: https://grokonez.com/reactive-programming/reactor/reactor-handle-error#21_By_falling_back_to_another_Flux

    onErrorResume will fall back to another Flux and let you catch and manage the exception thrown by previous Flux. if look into the implementation of onErrorReturn, you will find onErrorReturn is actually using onErrorResume.

    So here the code should be:

    .onErrorResume(throwable -> Mono.just(handleError(throwable)));
    
    0 讨论(0)
  • 2021-01-05 17:30

    Use Exceptions.propagate(e) which wraps a checked exception into a special runtime exception that can be handled by 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

    @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());
    }
    

    Output

    User [name=JHON, profession=10000]
    User [name=MIKE, profession=BOT]
    
    0 讨论(0)
  • 2021-01-05 17:32

    The last line of the code of @James Ralston is wrong. The correct code should be:

    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)));
    
    0 讨论(0)
  • DoOnError will only perform side effects and assuming the findById are will return a Mono.Error() if it fails something like this should work.

    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)));
    

    The try catch will only work if you either call a blocking operation of the chain, or a runtime error occurs before you enter the reactive chain. the doOn operations do not modify the chain, they are used for side effects only. Since flatMap expects a producer, you will need to return a Mono from the call, and in this case if an error occurs, then it will just propagate the error. In all reactive chains the error will propagate unless otherwise handled.

    0 讨论(0)
提交回复
热议问题