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.

¿Te ha resultado útil?

Promedio de puntuación 5 / 5. Recuento de votos: 1