3

I've read quite a few documentations and other stackoverflow questions regarding this matter but I can't seem to get my code working.

So essentially I have a WebClient making a POST request.

  • IF the response status is 200, then I make another call to another endpoint using a different WebClient. After second webclient call, return a string.
  • ELSE I just return a String from the method e.g. "failed to create order.".

Simple enough. (this is all done in a seperate thread fyi, not the main thread.)

But I've noticed that if i do get back a 500 error code, WebClient throws an exception. What I want to do is capture the exception and handle that gracefully and return a String like "Error calling first endpoint etc."

This is what I have so far:

private String generateOrder(ImportedOrderDetails importedOrderDetails)
   {
    
      Order requestBody = generateRequestBody(importedOrderDetails);
      OrderResponse responseForCreatingOrder = orderWebClient()
                                                       .post()
                                                       .body(Mono.just(requestBody), Order.class)
                                                       .retrieve()
                                                       .bodyToMono(OrderResponse.class)
                                                       .block();

      
      if (responseForCreatingOrder.getResponseStatus().equals(SUCCESS))
      {...other call using different webclient}
      else{ return "Error creating order."}

This works fine when the response status is 200 but when its 500 it blows up. OrderResponse is a custom object. orderWebClient() is just a method that returns a prebuilt WebClient containing the baseUrl and headers etc.

I came across this: Spring WebClient - How to handle error scenarios I did try implementing it but couldn't figure out where to put the block method since I kept on getting the following:

reactor.core.Exceptions$ReactiveException: java.lang.Exception
    at reactor.core.Exceptions.propagate(Exceptions.java:393)
    at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:97)
    at reactor.core.publisher.Mono.block(Mono.java:1680)

I had to edit my code a bit to try and implement the answer to that question:

   private Mono<? extends Throwable> handleError(String message) {
      log.error("====---"+message);
      return Mono.error(Exception::new);
   }

   private String generateOrder(ImportedOrderDetails importedOrderDetails)
   {
      Order requestBody = generateRequestBody(importedOrderDetails);
      Mono<OrderResponse> responseForCreatingDemo = orderWebClient()
                                                       .post()
                                                       .body(Mono.just(requestBody), Order.class)
                                                       .retrieve()
                                                       .onStatus(
                                                          (HttpStatus::is5xxServerError),
                                                          (it -> handleError(it.statusCode().getReasonPhrase()))
                                                       )
                                                       .bodyToMono(OrderResponse.class);


      
      System.out.println("-=-"+responseForCreatingDemo);
      if (responseForCreatingOrder != null && responseForCreatingOrder.block().getHeader().getResponseStatus().equals(SUCCESS)){...}

The error was coming from the .block part in the if condition. I believe this is something pretty trivial and missing the big picture.

Any suggestions?

Denis Zavedeev
  • 7,627
  • 4
  • 32
  • 53
bootlover123
  • 111
  • 1
  • 8
  • Can you please post `OrderResponse` class? – Denis Zavedeev Aug 29 '20 at 20:42
  • I'm away from my laptop but it's just essentially a simple pojo class that has Json annotations like `@JsonInclude(JsonInclude.Include.NON_NULL)` , `@JsonProperty` etc from the jackson lib. – bootlover123 Aug 29 '20 at 20:46
  • 1
    If you’re calling `block()` just use `RestTemplate`. You’re breaking the reactive chain anyway - there’s very little point to the additional complexity. – Boris the Spider Aug 29 '20 at 21:36

1 Answers1

0

It seems you have two kinds of statuses:

  1. Http status, defined by the protocol itself (see HTTP response status codes)
  2. Something specific to the application you're working on, encapsulated into the OrderResponse class.

So you have to handle two "errors" instead of one, one of the possible solutions might look like

.retrieve()
.bodyToMono(OrderResponse.class)
// 4xx, 5xx errors and return "Unable to create order" String instead
.onErrorContinue(WebClientResponseException.class, (ex, v) -> 
    Mono.just("Unable to create order"))
// if application specific status is not "ok" return "Unable to create order"
.map(it -> it.ok ? "Ok response" : "Unable to create order")
.block();

Please note that this code sample ignores exception and does not even log it

Denis Zavedeev
  • 7,627
  • 4
  • 32
  • 53