0

What i trying to achieve is to get my response error with 404 code and the error body with WebClient, how do i do this properly?

here is my response with error code 404 and the body response from another API :

{
  "timestamp": "2020-09-02T07:36:01.960+00:00",
  "message": "Data not found!",
  "details": "uri=/api/partnershipment/view"
}

and here is how my consuming code looked like :

    Map<String,Long> req = new HashMap<String,Long>();
    req.put("id", 2L);

    PartnerShipmentDto test = webClient.post()
    .uri(urlTest).body(Mono.just(req), PartnerShipmentDto.class)
    .exchange()
    .flatMap(res -> {
        if(res.statusCode().isError()){
            res.body((clientHttpResponse, context) -> {
                throw new ResourceNotFound(clientHttpResponse.getBody().toString());
            });
            throw new ResourceNotFound("aaaa");

        } else {
            return res.bodyToMono(PartnerShipmentDto.class);
        }
    })
    .block();

and here is my ResourNotFound.java class :

@SuppressWarnings("serial")
@ResponseStatus(HttpStatus.NOT_FOUND)
public class ResourceNotFound extends RuntimeException {
    
    public ResourceNotFound(String message){
        super(message);
    }
    
}

and here is my Global Exception handler using @ControllerAdvice :

@ControllerAdvice
@RestController
public class CustomResponseEntityExceptionHandler extends ResponseEntityExceptionHandler {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());

    public final ResponseEntity<Object> handleAllException(Exception ex, WebRequest request) {

        ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));
        logger.error(ex.getMessage());
        return new ResponseEntity(exceptionResponse, HttpStatus.INTERNAL_SERVER_ERROR);
    }

    @ExceptionHandler(ResourceNotFound.class)
    public final ResponseEntity<Object> handleResourceNotFoundException(ResourceNotFound ex, WebRequest request) {

        ExceptionResponse exceptionResponse = new ExceptionResponse(new Date(), ex.getMessage(), request.getDescription(false));
        logger.error(ex.getMessage());
        return new ResponseEntity(exceptionResponse, HttpStatus.NOT_FOUND);
    }

}

but the response i got printed in my ResourceNotFound exception is like this (this is my error from consumer side) :

{
  "timestamp": "2020-09-02T07:50:48.132+00:00",
  "message": "FluxMap",
  "details": "uri=/api/shipmentaddressgrouping/store"
}

it written "FluxMap" only, how i get the "message" field? i would like to get the "timestamp" and "details" field too

Ke Vin
  • 3,478
  • 11
  • 60
  • 91

1 Answers1

2

The main issue with the example code you have give is the following line of code

throw new ResourceNotFound(clientHttpResponse.getBody().toString());

The type of this is Flux<DataBuffer>, not the actual response body. This is leading to the issue you are seeing.

The way to solve this is invoking the bodyToMono method on the error response body and mapping to a java object. This can be done via the onStatus operator expose from the web client that allows you to take specific actions on specific status codes.

The code snippet below should resolve this

    webClient.post()
            .uri(uriTest).body(Mono.just(req), PartnerShipmentDto.class)
            .retrieve()
            .onStatus(HttpStatus::isError, res -> res.bodyToMono(ErrorBody.class)
                    .onErrorResume(e -> Mono.error(new ResourceNotFound("aaaa")))
                    .flatMap(errorBody -> Mono.error(new ResourceNotFound(errorBody.getMessage())))
            )
            .bodyToMono(PartnerShipmentDto.class)
            .block();

The class ErrorBody should contain all of the fields you want to map from json to the java object. The example below only maps the "message" field.

public class ErrorBody {
    private String message;

    public String getMessage() {
        return message;
    }

    public void setMessage(String message) {
        this.message = message;
    }
}
Michael McFadyen
  • 2,675
  • 11
  • 25
  • woah it solved my error message, just to clear my mind, i read somewhere which is said, if we use retreive(), we can't get the error status, it led my to change my code from retrieve() to exchange(), but u make retreive() worked, when should i use retrieve() or exchange()? – Ke Vin Sep 03 '20 at 07:43
  • 1
    I would advise to use retrieve() as much as possible as this is what is recommended in the javadocs. Read the note in the docs below for an explanation https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/web/reactive/function/client/WebClient.RequestHeadersSpec.html#exchange-- – Michael McFadyen Sep 04 '20 at 20:59
  • thank u very much for the explanation Michael, that made my day :) – Ke Vin Sep 05 '20 at 16:25