4
votes

In my webflux app I want to send some requests via WebClient. I want to handle all conditions (200, 401, 403 and ... response) and then response a json to client. for error status codes, I want to use @RestControllerAdvice, So I have to throw a custom exception and then in controller advice handle custom json. See the sample code:

WebClient.create().post
   .uri("URI")
   .retrieve()
   .onStatus(HttpStatus::is4xxClientError, {
       // create Mono<CustomException> 
   }
   .bodyToMono(ResponseDto.class)

And now exception handler is as follow:

@ResponseBody
@ResponseStatus(...)
@ExceptionHandler(CustomException1.class)
public void customException1(CustomException1 exception) {
   //do Something width response body
}

@ExceptionHandler(CustomException2.class)
public void customException2(CustomException2 exception) {
   //do Something width response body
}

Webclient get a 401 response woth json body as follow:

{
   "message": "Password is incorrect"
}

I can create Mono.error(new CustomException()), But the problem is WebClient response body. If the message be "Password is incorrect", I want to send client:

Username or Password is incorrect

How do I do?

2
Could you clarify what is the actual and expected result here? Do you want to change your exception handler or your web client code?Martin Tarjányi
@MartinTarjányi Users send an ajax request, and Whit request parameters I send a request to a server. I want to response users differnt answer in failed modes. So I want do it with Exception Handler.Morteza Malvandi
There are multiple ways of solving this and you have provided too little information. What is the actual problem you want to solve, and if you are new in webflux why do you want to So I want do it with Exception Handler. Webflux is not like spring boot, you solve rthings different using webflux. if you explain what it is you want to do more in detail when an exception is thrown. Do want to log? return a http status to the client? do a new request?Toerktumlare

2 Answers

3
votes

Please check the following API implementation which handles the error response of the invoked rest api call. I am assuming that we know the api's response so that I have created the ResponseDTO class.

Controller.java

@GetMapping("/webclient/test")
public ResponseDTO testWebclient(@RequestBody RequestDTO requestPayload) {
    String url = "http://localhost:8080/api/write/simple";
    return WebClient.create()
            .post()
            .uri(url)
            .accept(MediaType.APPLICATION_JSON)
            .body(BodyInserters.fromObject(requestPayload))
            .retrieve()
            .onStatus(HttpStatus::is4xxClientError, response -> {
                return response.bodyToMono(ResponseDTO.class).flatMap(error -> {
                    return Mono.error(new CustomRuntimeException(error.getMessage(), response.statusCode(), error.getErrorCode()));
                });
            })
            .onStatus(HttpStatus::is5xxServerError, response -> {
                return response.bodyToMono(ResponseDTO.class).flatMap(error -> {
                    return Mono.error(new CustomRuntimeException(error.getMessage(), response.statusCode()));
                });
            })
            .bodyToMono(ResponseDTO.class);
}

ResponseDTO.java

public class ResponseDTO {

    private String message;
    private HttpStatus status;
    private ErrorCode errorCode;
    
    public ResponseDTO(String message, HttpStatus status) {
        this.message = message;
        this.status = status;
    }

    public ResponseDTO(String message, HttpStatus status, ErrorCode errorCode) {
        this.message = message;
        this.status = status;
        this.errorCode = errorCode;
    }
    /** getters **/
}

ErrorCode.java

    public class ErrorCode {
        private String numCode;
        private String txtCode;
        
        /** getters **/
    }

To catch the exception please add the following code snippet (Hope you knew it already) in @RestControllerAdvice's class.

@ExceptionHandler(value = CustomRuntimeException.class)
public final ResponseEntity<ApiResponse> handleCustomRuntimeException(CustomRuntimeException exception) {
    List<ApiSubError> subErrors = Arrays.asList(new ApiSubError(exception.getMessage()));
    ApiResponse response = new ApiErrorResponse(exception.getMessage(), exception.getErrorCode());
    LOG.error("result: {}", response.getResult());
    return new ResponseEntity<>(response, exception.getHttpStatus());
}
1
votes

I never used WebFlux, but I have found Add Exception handler for Spring Web Client. It looks like you could throw an exception by writing a return Mono.error(new CustomException1()); inside onStatus handler function. According to description, exception will be thrown from bodyToMono(Class) method.

Finally, you could modify your ControllerAdvice like this:

@ExceptionHandler(CustomException.class)
public ResponseEntity<CustomErrorModel> customException(CustomException exception) {
   return new ResponseEntity<>( new CustomErrorModel(), HttpStatus.BAD_REQUEST );
}

As I said - I didn't test it, but it could be a hint for you.