Looks like easy task and basic feature but I'm not able to figure out how to solve this problem. I've read multiple sources, tried multiple solutions, but no luck.
I have two spring-boot rest apps let's name them: ProxyApp & BusinessApp. ProxyApp is called by Angular frontend and contains Swagger annotations to generate TS/Angular client. BusinessApp has business logic and is returning OK response or ErrorResponse (used Spring @ControllerAdvice). I need to route both: body and also http status from BusinessApp via ProxyApp. But WebClient re-sets status (e.g. 400) coming from BusinessApp to 200.
@Operation(summary = "Some desc")
@PostMapping(value = "/", consumes = {MediaType.APPLICATION_JSON_VALUE}, produces = {MediaType.APPLICATION_JSON_VALUE})
@ApiResponse(responseCode = "200", content = {
@Content(mediaType = "application/json", schema = @Schema(implementation = User.class))
})
@ResponseStatus(HttpStatus.OK)
public String someMethod() {
// skipped
return webClient.post()
.uri(url)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(request)
.retrieve()
.onStatus(HttpStatus::isError, clientResponse -> Mono.empty())
.bodyToMono(String.class)
.onErrorMap(WebClientRequestException.class, ex -> new RestApiUnavailableException(ex,"some msg"))
.block();
}
Your webClient code returns 200 because of the line .onStatus(HttpStatus::isError, clientResponse -> Mono.empty())
.
If you return Mono.empty()
while handling the error status, it will suppress the error and proceed with the normal response, as described in the documentation of onStatus()
method:
To suppress the treatment of a status code as an error and process it as a normal response, return Mono.empty() from the function. The response will then propagate downstream to be processed.
On error response, WebClient throws WebClientResponseException
by default. It contains all the required information, so in order to pass the error response through the proxy just create an exception handler as follows:
@ControllerAdvice
public class ErrorHandler {
@ExceptionHandler(WebClientResponseException.class)
public ResponseEntity<String> handle(WebClientResponseException e) {
return ResponseEntity.status(e.getStatusCode().value()).body(e.getResponseBodyAsString());
}
}
Make sure to get remove your onStatus()
call so that your call looks like that:
return webClient.post()
.uri(url)
.contentType(MediaType.APPLICATION_JSON)
.bodyValue(request)
.retrieve()
.bodyToMono(String.class)
.onErrorMap(WebClientRequestException.class, ex -> new RestApiUnavailableException(ex,"some msg"))
.block();
That way, you will intercept the exception thrown by WebClient and pass the response with the appropriate status code and body.
As a side note, keep in mind that calling block()
denies the benefits of reactive stack and is generally discouraged.