WebExceptionHandler : How to write a body with Spring Webflux

Viewed 19331

I want to handle the Exception of my api by adding a WebExceptionHandler. I can change the status code, but I am stuck when i want to change the body of the response : ex adding the exception message or a custom object.

Does anyone have exemple ?

How I add my WebExceptionHandler :

HttpHandler httpHandler = WebHttpHandlerBuilder.webHandler(toHttpHandler(routerFunction))
  .prependExceptionHandler((serverWebExchange, exception) -> {

      exchange.getResponse().setStatusCode(myStatusGivenTheException);
      exchange.getResponse().writeAndFlushWith(??)
      return Mono.empty();

  }).build();
5 Answers

for anyone looking at a way to write JSON response body, here's a Kotlin code sample:

fun writeBodyJson(body: Any, exchange: ServerWebExchange) =
    exchange.response.writeWith(
        Jackson2JsonEncoder().encode(
            Mono.just(body),
            exchange.response.bufferFactory(),
            ResolvableType.forInstance(body),
            MediaType.APPLICATION_JSON_UTF8,
            Hints.from(Hints.LOG_PREFIX_HINT, exchange.logPrefix)
        )
    )

not 100% sure that's the way to go though, would like to get some opinions.

I needed to render a view and couldn't find a way to do this via the exception handler (only found examples using controllers which would require a needless redirect) so I used the ServerResponseResultHandler to render it from the handler. This gives the required Mono<Void> response.

/** Used when creating the {@link HandlerResult}
 *  Taken from https://github.com/spring-projects/spring-framework/blob/5.3.x/spring-webflux/src/main/java/org/springframework/web/reactive/function/server/support/HandlerFunctionAdapter.java
 */
private static final MethodParameter RETURN_TYPE;

static {
    try {
        Method method = ServerAccessDeniedHandler.class.getMethod("handle", ServerWebExchange.class, AccessDeniedException.class);
        RETURN_TYPE = new MethodParameter(method, -1);
    }
    catch (NoSuchMethodException ex) {
        throw new IllegalStateException(ex);
    }
}

@Autowired
private ServerResponseResultHandler responseHandler;

public class CustomAccessDeniedHandler implements ServerAccessDeniedHandler {
    @Override
    public Mono<Void> handle(ServerWebExchange exchange, AccessDeniedException ex) {
        return ReactiveSecurityContextHolder.getContext()
            .map(SecurityContext::getAuthentication)
            .filter(auth -> auth != null && auth.isAuthenticated())
            .switchIfEmpty(Mono.empty())
            .flatMap(auth -> ServerResponse
                .status(HttpStatus.FORBIDDEN)
                // Give name of view and model attributes it requires
                .render("unauthorized", Map.of("varName", varValue))
                .flatMap(response -> responseHandler.handleResult(exchange,
                    new HandlerResult(this, response, RETURN_TYPE)))
            );
    }
}
Related