Home > Software design >  How throws CustomException from reactive service to controller?
How throws CustomException from reactive service to controller?

Time:11-30

I am receiving a request in a 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 {
  
            return Mono.fromCallable(() -> userRepository.findByRawId(statusRequest.getUserId()))                               
                    .subscribeOn(Schedulers.boundedElastic())
                    .switchIfEmpty(
                            Mono.error(new CheckCardStatusException(HttpStatus.NOT_FOUND)))
                    .flatMap(user -> {
                        return null;
                    });
    }

My CustomException:

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

CodePudding user response:

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.

CodePudding user response:

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.

  • Related