20

I'm developing some reactive microservices using Spring Boot 2 and Spring 5 with WebFlux reactive starter.

I'm facing the following problem: I want to handle all HTTP Statuses that I receive from calling another REST Services and throws an exception when I receive some bad HTTP Status. For example, when I call an endpoint and I receive an 404 HTTP Status, I want to throw an exception and that exception to be handled in some ExceptionHandler class, just like the way it was in Spring 4 with @ControllerAdvice.

What is the right way to do this? Hope to receive some good suggestions.

Dina Bogdan
  • 4,345
  • 5
  • 27
  • 56
  • You can also refer -https://stackoverflow.com/questions/43575538/what-is-the-right-way-to-handle-errors-in-spring-webflux/45280499#45280499 https://stackoverflow.com/questions/49137253/how-could-we-use-exceptionhandler-with-spring-web-flux – DAIRAV Apr 04 '18 at 10:44
  • You can also refer, https://stackoverflow.com/questions/49137253/how-could-we-use-exceptionhandler-with-spring-web-flux https://stackoverflow.com/questions/43575538/what-is-the-right-way-to-handle-errors-in-spring-webflux/45280499#45280499 – DAIRAV Apr 04 '18 at 10:46

2 Answers2

17

This can be addressed in two independent parts.

How to convert HTTP 404 responses received by WebClient into custom exceptions

When using WebClient, you can receive HTTP 404 responses from remote services. By default, all 4xx and 5xx client responses will be turned into WebClientResponseException. So you can directly handle those exceptions in your WebFlux app.

If you'd like to turn only 404 responses into custom exceptions, you can do the following:

WebClient webClient = //...
webClient.get().uri("/persons/1")
  .retrieve()
  .onStatus(httpStatus -> HttpStatus.NOT_FOUND.equals(httpStatus),
                        clientResponse -> Mono.error(new MyCustomException()))
  .bodyToMono(...);

This is obviously done on a per client call basis.

You can achieve the same in a more reusable way with an ExchangeFilterFunction that you can set once and for all on a WebClient instance like this:

WebClient.builder().filter(myExchangeFilterFunction)...

How to handle custom exceptions in WebFlux apps

With Spring WebFlux with annotations, you can handle exceptions with methods annotated with @ExceptionHandler (see Spring Framework reference documentation).

Note: using a WebExceptionHandler is possible, but it's quite low level as you'll have no high-level support there: you'll need to manually write the response with buffers without any support for serialization.

Brian Clozel
  • 56,583
  • 15
  • 167
  • 176
  • Great answer, but could you give more details on how exactly you get the exception message of the remote services in an exchangeFilterFunction? I have been trying to figure this out for some time now without success. I think it would be very helpful and will provide a more detailed answer. – Zorie Jun 08 '18 at 15:25
  • I don't understand - you don't get an exception message from the remote service, you just get an HTTP response. Maybe this should be a new question? – Brian Clozel Jun 08 '18 at 15:28
  • Oh, yes, sorry. What I meant is once you get a response with status code 4xx or 5xx and you try to handle it in an ExchangeFilterFunction, how exactly are you able to get the body of the response(which actually contains the error message)? As you pointed out, I would like to handle all unsuccessful requests within the WebClient and not per client call. – Zorie Jun 08 '18 at 15:37
  • How do I handle a `WebClientResponseException` in a non-Webflux app? My understanding is that `@ExceptionHandler` is only supported in contollers. – Daniel Nitzan Mar 14 '19 at 14:33
  • Could you ask this in another question? It’s not easy to answer in comments. – Brian Clozel Mar 14 '19 at 14:51
  • @Zorie 2 years later and still rebuffed... I am wondering the same thing, and haven't gotten anywhere – Blake Neal Mar 13 '20 at 19:55
  • @BrianClozel is this still the right way of handling WebClient exceptions globally? – Shubham Jan 05 '21 at 14:31
6

I think what you are looking for is WebFluxResponseStatusExceptionHandler the check this for reference.

In the WebHandler API, a WebExceptionHandler can be used to to handle exceptions from the chain of WebFilter's and the target WebHandler. When using the WebFlux Config, registering a WebExceptionHandler is as simple as declaring it as a Spring bean, and optionally expressing precedence via @Order on the bean declaration or by implementing Ordered.

This example may help, have not tried it myself.

@Component
@Order(-2)
class RestWebExceptionHandler implements WebExceptionHandler{

    @Override
    public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
        if (ex instanceof PostNotFoundException) {
            exchange.getResponse().setStatusCode(HttpStatus.NOT_FOUND);

            // marks the response as complete and forbids writing to it
            return exchange.getResponse().setComplete();
        }
        return Mono.error(ex);
    }
}

class PostNotFoundException extends RuntimeException {
    PostNotFoundException(String id) {
        super("Post:" + id + " is not found.");
    }
}
Pär Nilsson
  • 2,259
  • 15
  • 19
  • Yeah, ur right. I've already found that, but I didn't saw any implementation of it. Can you provide me something like that? – Dina Bogdan Apr 04 '18 at 10:39
  • I think that this will fit my problem. – Dina Bogdan Apr 04 '18 at 10:58
  • @Par Nilsson, thanks for this example. For people who are struggling with this, the above example will help adding a status code. If you want to add a message as well, add: ``` (serverWebExchange, exception) -> { exchange.getResponse().setStatusCode(myStatusGivenTheException); byte[] bytes = "Some text".getBytes(StandardCharsets.UTF_8); DataBuffer buffer = exchange.getResponse().bufferFactory().wrap(bytes); return exchange.getResponse().writeWith(Flux.just(buffer)); } ``` – Omkar Apr 20 '20 at 13:03