简体   繁体   中英

How throws CustomException from reactive service to controller?

I am receiving a request in a 0. RestController and give it to the service for processing. If service doesnt throw exception , i just return HttpStatus.200 , but if an exception occurs in the service, i need catch it in controller and return the status depending on the exception. Inside service, i need to use Mono.fromCallable for repo access. Well, if user not found, i try throw my CustomException , but i cant cach it in controller. What am I doing wrong?

Controller:

@RestController
@RequiredArgsConstructor
@Slf4j
public class CardStatusController {

    private final CardStatusService cardStatusService;

    @PostMapping(value = "api/{user_id}/confirm", produces = {MediaType.APPLICATION_JSON_VALUE})
  
    public Mono<ResponseEntity<HttpStatus>> confirmStatus(
            @PathVariable("user_id") String userId,
            @RequestBody CardStatusRequest statusRequest) {
        
        statusRequest.setUserId(userId);
        
        cardStatusService.checkCardStatus(statusRequest);

        return Mono.just(new ResponseEntity<>(HttpStatus.OK));
    }

    @ExceptionHandler({ CheckCardStatusException.class })
    public void handleException() {
        log.error("error!");
    }

My service:

    public Mono<Void> checkCardStatus(CardStatusRequest statusRequest) throws CheckCardStatusException {
        if (statusRequest.getCardEmissionStatus().equals(CardEmissionStatus.ACCEPTED)) {
            String reference = statusRequest.getReference();
            return Mono.fromCallable(() -> userRepository.findById(statusRequest.getUserId()))
                    .subscribeOn(Schedulers.boundedElastic())
                    .switchIfEmpty(Mono.error(new CheckCardStatusException(HttpStatus.NOT_FOUND)))
                    .flatMap(user -> Mono.fromCallable(() -> cardRepository.findFireCardByUserId(user.getId()))
                            .flatMap(optionalCard -> {
                                if (optionalCard.isPresent()) {
                                    if (optionalCard.get().getExtId().isEmpty()) {
                                        Card card = optionalCard.get();
                                        card.setExtId(reference);
                                        try {
                                            cardRepository.save(card);
                                        } catch (Exception e) {
                                            return Mono.error(new CheckCardStatusException(HttpStatus.INTERNAL_SERVER_ERROR));
                                        }
                                    } else {
                                        if (!optionalCard.get().getExtId().equals(reference)) {
                                                return Mono.error(new CheckCardStatusException(HttpStatus.CONFLICT));
                                            }
                                    }
                                } else {
                                    Card card = new Card();
                                    //set card params
                                    try {
                                        cardRepository.save(card);
                                    } catch (Exception e) {
                                        return Mono.error(new CheckCardStatusException(HttpStatus.INTERNAL_SERVER_ERROR));
                                    }
                                }
                                return Mono.error(new CheckCardStatusException(HttpStatus.OK));
                            })).then();
        }
        else {
            return Mono.error(new CheckCardStatusException(HttpStatus.OK));
        }
    }
}

My CustomException:

@AllArgsConstructor
@Getter
public class CheckCardStatusException extends RuntimeException {
    private HttpStatus httpStatus;
}

The Mono returned by checkCardStatus is never subscribed, so the error signal is ignored. You have to return the whole chain to the Webflux framework as follows:

public Mono<ResponseEntity<HttpStatus>> confirmStatus(
        @PathVariable("user_id") String userId,
        @RequestBody CardStatusRequest statusRequest) {
    
    statusRequest.setUserId(userId);
    
    return cardStatusService.checkCardStatus(statusRequest)
            .then(Mono.just(new ResponseEntity<>(HttpStatus.OK)));
}

In case of an error, the corresponding ExceptionHandler will be executed.

Since you are using Mono.error(Throwable t) , the exception is not actually thrown. Because of that, your Error Handler will never be called. Instead, an onError signal is emitted, which can be handled as part of your cardStatusService.checkCardStatus(statusRequest); call in the RequestMapping . In order to do that, you actually need to subscribe to the Mono:

cardStatusService.checkCardStatus(statusRequest).subscribe(
  successValue -> System.out.println("success");
  error -> // your error handling here
);

See: https://projectreactor.io/docs/core/release/api/reactor/core/publisher/Mono.html#subscribe-java.util.function.Consumer-java.util.function.Consumer-

For that reason, when using Mono in your service, you should never return null. That defeats the purpose of using Mono, since you can not subscribe to null. Maybe reconsider, if a publisher pattern is necessary here.

If you do not want to use Mono, and instead use the exception handler, explicitly throw the Exception either directly in the service or in your error handling subscription.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM