Search code examples
javaspringspring-webclient

How to call a method inside onStatus() in Spring Webclient


Currently, I just throw an exception in onStatus() when the response code is 4XX or 5XX. However, I want to call another service ( a compensating transaction to undo the changes) and then throw an Exception.

webclient
            .post()
            .uri(url, chargeSeqId)
            .contentType(MediaType.APPLICATION_JSON)
            .accept(MediaType.APPLICATION_JSON)
            .acceptCharset(Charset.forName("UTF-8"))
            .header(HttpHeaders.AUTHORIZATION, "Bearer " + token)
            .body(BodyInserters.fromValue(pojos))
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError,
                    res -> res.bodyToMono(String.class)
                            .flatMap(error -> Mono.error(new DataValidationException(error))))
            .onStatus(HttpStatus::is5xxServerError,
                    res -> res.bodyToMono(String.class).flatMap(error -> Mono.error(new SystemException(error))))
            .bodyToMono(MyPojos[].class)
            .block();

I want to call the following method when the response code is either 4XX or 5XX before throwing either DataValidationException() or SystemException().

    private String deleteTransaction(Integer transactionID) {
        Mono<String> result = webclient.delete()
                .uri(uriBuilder -> uriBuilder
                        .path(url + "/" + transactionID + "/delete").build())
                .header(HttpHeaders.AUTHORIZATION, "Bearer " + clientProfile.getJwt().getTokenValue()).retrieve()
                .bodyToMono(String.class);
        String ack = result.block();
        return ack;
}

Solution

  • Instead of calling block inside another block you can make deleteTransaction method return mono.

    private Mono<String> deleteTransaction(Integer transactionID) {
            return webclient.delete()
                    .uri(uriBuilder -> uriBuilder
                            .path(url + "/" + transactionID + "/delete").build())
                    .header(HttpHeaders.AUTHORIZATION, "Bearer " + clientProfile.getJwt().getTokenValue()).retrieve()
                    .bodyToMono(String.class);
    }
    

    Then you can call this method in the required places in non blocking way. Also as I donot know what you actually want to pass to deleteTransaction method, I have passed chargeSeqId to it.

    
         .onStatus(HttpStatus::is4xxClientError,
                 res -> {
                        var ackMono = deleteTransaction(chargeSeqId); // replace with required input
                        var resBodyMono = res.bodyToMono(String.class);
                        return Mono.zip(ackMono, resBodyMono, (ack,resBody)->Mono.error(new DataValidationException(resBody)));
                        })
                                 
    

    This can be done for is5xxServerError as well.