Home > Software design >  Handling exception with Mono flow
Handling exception with Mono flow

Time:05-07

I have a WebFlux handler as below.

@Transactional
  public Mono<ServerResponse> submitOrder(final ServerRequest request) {
    return context.retrieveUser().flatMap(usr -> {
      try {
        return Mono.zip(branchSetting, labOrderDetail, labOrderTests).flatMap(response -> {
          final Mono<String> submitOrderMono = service.submitOrder(usr);
          final Mono<Integer> updateStatusMono = orderRepository.updateStatus(orderId);
          return Mono.zip(submitOrderMono, updateStatusMono).flatMap(submitResponse -> {
            return ok().bodyValue(submitResponse.getT1());
          }).onErrorResume(e -> {
            if (e instanceof ServiceException) {
              ServiceException ex = (ServiceException) e;
              return status(ex.getStatusCode()).bodyValue(e.getMessage());
            } else {
              return status(500).bodyValue(e.getMessage());
            }
          });
        });
      } catch (Throwable e) {
        if (e instanceof ServiceException) {
          ServiceException ex = (ServiceException) e;
          return status(ex.getStatusCode()).bodyValue(e.getMessage());
        } else {
          return status(500).bodyValue(e.getMessage());
        }
      }
    });
  }

submitOrder method from service class,

public Mono<String> submitOrder(final Order order,
      if (order.getPatientId() != null) {
        throw new ServiceException("Missing Id for patient !!!", HttpStatus.BAD_REQUEST.value());
      }
  }

Here, I am doing some validation and throwing Exception.

But, this exception is not getting into one rrorResume or catch block in the calling main method and hence the service caller sees 500 error.

Not sure what is wrong here.

CodePudding user response:

When working in a reactive WebFlux context, throwing exceptions and using try-catch-block is, imho, not really best practice.

The more idiomatic approach would be to use Mono.error() instead of throw commands. Mono.error() emits an error signal so that a subsequent onErrorResume() could deal with it.

That being said, submitOrder() could look like this:

    public Mono<String> submitOrder(final Order order) {
        if (order.getPatientId() == null) {
           Mono.error(new ServiceException("Missing Id for patient !!!", 500));
        }
       return Mono.just("some reasonable result");
    }

With this rewrite, the first snippet should (maybe with some minor adjustments) work this way:

    public Mono<ServerResponse> submitOrder(final ServerRequest request) {
        return context.retrieveUser().flatMap(usr -> {
                return Mono.zip(branchSetting, labOrderDetail, labOrderTests).flatMap(response -> {
                    final Mono<String> submitOrderMono = service.submitOrder(usr);
                    final Mono<Integer> updateStatusMono = orderRepository.updateStatus(orderId);
                    return Mono.zip(submitOrderMono, updateStatusMono).flatMap(submitResponse -> {
                        return ok().bodyValue(submitResponse.getT1());
                    }).onErrorResume(e -> {
                        if (e instanceof ServiceException) {
                            ServiceException ex = (ServiceException) e;
                            return status(ex.getStatusCode()).bodyValue(e.getMessage());
                        } else {
                            return status(500).bodyValue(e.getMessage());
                        }
                    });
                });
        });
    }
  • Related