
Centraliza el manejo de errores en Spring Boot con @ControllerAdvice
En el desarrollo de APIs REST con Spring Boot, mantener una arquitectura limpia y coherente es clave. Uno de los patrones más efectivos para lograrlo es el uso de @ControllerAdvice, una anotación que permite centralizar la gestión de errores y otras preocupaciones transversales.
En este artículo, exploramos cómo usar @ControllerAdvice de forma efectiva para capturar excepciones personalizadas, estandarizar respuestas y mejorar la trazabilidad de nuestras aplicaciones.
¿Qué es @ControllerAdvice?
@ControllerAdvice es una anotación de Spring que permite aplicar lógica compartida a todos los controladores (@Controller o @RestController). Su uso más común es capturar y procesar excepciones lanzadas desde cualquier punto del flujo web, evitando la necesidad de try/catch en cada endpoint.
Caso típico: errores funcionales con excepciones personalizadas
En aplicaciones con lógica de negocio compleja, es común lanzar excepciones personalizadas como AbsisException, acompañadas de códigos de error y descripciones legibles. Veamos un ejemplo práctico.
Definimos un tipo de error centralizado:
public enum AbsisErrorType {
CONFLICT_STATUS_WEIGHT("CONFLICT_STATUS_WEIGHT", "Error al obtener el peso del estado", HttpStatus.CONFLICT);
private final String code;
private final String message;
private final HttpStatus status;
// constructor, getters
}
Creamos nuestra excepción personalizada:
public class AbsisException extends RuntimeException {
private final String code;
private final String message;
private final HttpStatus status;
private AbsisException(AbsisErrorType errorType) {
super(errorType.getMessage());
this.code = errorType.getCode();
this.message = errorType.getMessage();
this.status = errorType.getStatus();
}
public static AbsisException of(AbsisErrorType errorType) {
return new AbsisException(errorType);
}
// getters
}
Lanzamos la excepción en una validación:
if (!estadoValido) {
throw AbsisException.of(AbsisErrorType.CONFLICT_STATUS_WEIGHT);
}
Centralizando el manejo de errores con @ControllerAdvice
En lugar de capturar manualmente cada error en el controlador, delegamos su procesamiento a una clase especializada.
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(AbsisException.class)
public ResponseEntity<ErrorResponse> handleAbsisException(AbsisException ex) {
var error = new ErrorResponse(ex.getCode(), ex.getMessage());
return ResponseEntity.status(ex.getStatus()).body(error);
}
}
Estructura de respuesta estandarizada:
public class ErrorResponse {
private final String code;
private final String message;
public ErrorResponse(String code, String message) {
this.code = code;
this.message = message;
}
// getters
}
¿Qué ventajas aporta?
- Consistencia en la salida de errores: Todos los errores tienen el mismo formato: código, mensaje y status HTTP.
- Separación de responsabilidades: Los controladores se enfocan en lógica funcional. El manejo de errores se centraliza.
- Escalabilidad: Puedes añadir trazas, logs, métricas o traducción de errores en un solo punto sin tocar cada controlador.
- Testeo simplificado: Al separar la lógica de error, se facilita la cobertura de pruebas unitarias y de integración.
Una experiencia personal
En uno de los primeros proyectos donde usábamos Spring Boot, manejábamos errores con múltiples try-catch dentro de los controladores. Cuando el proyecto empezó a escalar, esa estrategia se volvió insostenible: cada cambio en los mensajes o en la lógica de error implicaba tocar múltiples endpoints. El cambio a @ControllerAdvice fue clave para estabilizar la arquitectura y estandarizar la salida de errores.
Mejores prácticas
- Define un catálogo único de códigos de error (enum), esto reducirá la redundancia en el código.
- Usa HttpStatus de forma coherente en tus errores.
- Evita exponer detalles internos en entornos productivos.
- Considera incluir traceId o timestamp en el DTO de error para facilitar la trazabilidad.
Opinión:
Personalmente, considero que si una API REST maneja más de cinco errores funcionales diferentes, usar @ControllerAdvice ya no es una recomendación: es una necesidad. Ayuda a mantener la arquitectura limpia, y permite aplicar observabilidad, traducción o incluso políticas de reintento desde un único lugar. En general todo lo que aporte centralización en el manejo de los conceptos inherentes al desarrollo es una práctica que considero recomendable.