Mastering Global Exception Handling in Spring Cloud Gateway
This article explains why traditional @ControllerAdvice fails in Spring Cloud Gateway, introduces the built‑in ExceptionHandlingWebHandler and DefaultErrorWebExceptionHandler, and shows how to override them with a custom GlobalExceptionConfiguration for robust reactive error handling.
Why Global Exception Handling Is Needed
In a traditional Spring Boot application we use
@ControllerAdviceto handle exceptions globally and wrap responses uniformly.
<code>// excerpt from spring cloud alibaba console module
@ControllerAdvice
public class ConsoleExceptionHandler {
@ExceptionHandler(AccessException.class)
private ResponseEntity<String> handleAccessException(AccessException e) {
return ResponseEntity.status(HttpStatus.FORBIDDEN).body(e.getErrMsg());
}
}
</code>For example, a database exception can be caught by
@ControllerAdviceand returned to the client.
However, in a micro‑service architecture, when a gateway fails to forward a request to a downstream service (e.g., network error, service unavailable), the request never reaches the application, so
@ControllerAdviceis ineffective because the traffic never hits the service.
When all route predicates fail (404), the gateway returns the default Spring Boot error page, which cannot be handled by
@ControllerAdvicebecause Spring Cloud Gateway is built on WebFlux.
Solution
Default Processing Flow
ExceptionHandlingWebHandler, as the core WebHandler of Spring Cloud Gateway, performs exception filtering.
<code>public class ExceptionHandlingWebHandler extends WebHandlerDecorator {
@Override
public Mono<Void> handle(ServerWebExchange exchange) {
Mono<Void> completion;
try {
completion = super.handle(exchange);
} catch (Throwable ex) {
completion = Mono.error(ex);
}
// invoke global WebExceptionHandler chain
for (WebExceptionHandler handler : this.exceptionHandlers) {
completion = completion.onErrorResume(ex -> handler.handle(exchange, ex));
}
return completion;
}
}
</code>Default implementation:
DefaultErrorWebExceptionHandler<code>public class DefaultErrorWebExceptionHandler {
@Override
protected RouterFunction<ServerResponse> getRoutingFunction(ErrorAttributes errorAttributes) {
// decide response based on Accept header; browsers get HTML page
return route(acceptsTextHtml(), this::renderErrorView)
.andRoute(all(), this::renderErrorResponse);
}
}
</code> <code>// simulate Accept header
curl --location --request GET 'http://localhost:9999/adminx/xx' \
--header 'Accept: application/json'
{"timestamp":"2020-05-24 18:09:24","path":"/adminx/xx","status":404,"error":"Not Found","message":null,"requestId":"083c48e3-2"}
</code>Override ErrorWebExceptionHandler
<code>/**
* Global exception handler for the gateway (WebFlux environment).
* Must have lower priority than ResponseStatusExceptionHandler.
*/
@Slf4j
@Order(-1)
@RequiredArgsConstructor
public class GlobalExceptionConfiguration implements ErrorWebExceptionHandler {
private final ObjectMapper objectMapper;
@Override
public Mono<Void> handle(ServerWebExchange exchange, Throwable ex) {
ServerHttpResponse response = exchange.getResponse();
if (response.isCommitted()) {
return Mono.error(ex);
}
response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
if (ex instanceof ResponseStatusException) {
response.setStatusCode(((ResponseStatusException) ex).getStatus());
}
return response.writeWith(Mono.fromSupplier(() -> {
DataBufferFactory bufferFactory = response.bufferFactory();
try {
return bufferFactory.wrap(objectMapper.writeValueAsBytes(R.failed(ex.getMessage())));
} catch (JsonProcessingException e) {
log.warn("Error writing response", ex);
return bufferFactory.wrap(new byte[0]);
}
}));
}
}
</code>Summary
The overridden
DefaultErrorWebExceptionHandlermust have lower priority than the built‑in
ResponseStatusExceptionHandlerto correctly map error classes to response codes.
Additional extensions (e.g.,
SentinelBlockExceptionHandler) can be integrated, but overall they behave similarly to the default error handling.
Environment: Spring Cloud Hoxton.SR4 & Spring Boot 2.3.0.
Full implementation code reference: https://gitee.com/log4j/pig
Java Architecture Diary
Committed to sharing original, high‑quality technical articles; no fluff or promotional content.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.