Hola amante de las APIs limpitas y efectivas,
Hoy vengo con otro de esos temas que casi nadie te explica; que se suele montar al principio del proyecto, que es CLAVE para todos los proyectos, y que encima nadie te ha explicado cómo funciona.
Y sí, estoy hablando nada más y nada menos de nuestro famoso @RestControllerAdvice
O GlobalExceptionHandler
, para los amigos.
(Bueno, si te has quedado igual que antes…. sigue leyendo)
¿Por qué necesito esto en mi vida?
Pues porque cuando algo falla en tu aplicación (y va a fallar, créeme), no puedes devolver un 500 y una traza de 3 páginas al cliente.
O al revés, capturas el error en el servidor y devolver en la respuesta que todo 200 OK.
Necesitas una forma limpia, centralizada y elegante de manejar errores.
Y ahí entra en juego nuestro amego:
@RestControllerAdvice
(o el GlobalExceptionHandler
)
Pero es que en la publicación de hoy, no solo te voy a hablar de él…. Te voy a explicar cómo se monta.
¿Y qué vamos a montar?
Una mini estructura que:
✅ Capture todas las excepciones desde un único punto
✅ Devuelva una respuesta clara y amigable al cliente
✅ Te permita controlar el código de estado, el mensaje, e incluso un timestamp si te pones pro
Shhh! ¡Al lío!
Paso 1: Crear tu clase de error de respuesta
public record ErrorResponse(
String mensaje,
String ruta,
LocalDateTime timestamp
) {}
Sí, usamos record
, que para este tipo de DTOs es perfecto.
Y aquí es importante definir esta clase, porque va a ser el objeto que vamos a devolver siempre que se dé un error en nuestra aplicación.
Además, cuantos más campos, más información pasaremos.
Paso 2: Crear tu clase GlobalExceptionHandler
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(IllegalArgumentException.class)
public ResponseEntity<ErrorResponse> manejarIllegalArgument(IllegalArgumentException ex, HttpServletRequest request) {
ErrorResponse error = new ErrorResponse(
ex.getMessage(),
request.getRequestURI(),
LocalDateTime.now()
);
return ResponseEntity.badRequest().body(error);
}
@ExceptionHandler(Exception.class)
public ResponseEntity<ErrorResponse> manejarExcepcionesGenericas(Exception ex, HttpServletRequest request) {
ErrorResponse error = new ErrorResponse(
"Ha ocurrido un error inesperado",
request.getRequestURI(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.INTERNAL_SERVER_ERROR).body(error);
}
}
👉 Como ves, puedes tener un método para cada tipo de excepción. (te aviso porque esto crecerá… y mucho)
Y puedes devolver el código que quieras: 400
, 404
, 500
, etc.
¿Y cómo le llega esto al Cliente?
Pues mi fácil:
{
"mensaje": "El nombre no puede estar vacío",
"ruta": "/api/usuarios",
"timestamp": "2024-06-10T09:37:12"
}
Mucho mejor que un 500
con toda la traza del NullPointer de turno.
¿Y cómo se lanza esta excepción?
Pues realmente es en cualquier parte de tu aplicación, ya que si lanzas una excepción en la capa service o repository y no la capturas con un try - catch, esta excepción acabará llegando al @RestControllerAdvice
Imagina un endpoint así:
@PostMapping("/usuarios")
public ResponseEntity<Void> crearUsuario(@RequestBody UsuarioRequest request) {
if (request.nombre() == null || request.nombre().isBlank()) {
throw new IllegalArgumentException("El nombre no puede estar vacío");
}
// lógica para crear el usuario
return ResponseEntity.status(HttpStatus.CREATED).build();
}
¿Quieres ir más allá?
Puedes crear tus propias excepciones personalizadas:
public class RecursoNoEncontradoException extends RuntimeException {
public RecursoNoEncontradoException(String mensaje) {
super(mensaje);
}
}
Y luego:
@ExceptionHandler(RecursoNoEncontradoException.class)
public ResponseEntity<ErrorResponse> manejarNoEncontrado(RecursoNoEncontradoException ex, HttpServletRequest request) {
ErrorResponse error = new ErrorResponse(
ex.getMessage(),
request.getRequestURI(),
LocalDateTime.now()
);
return ResponseEntity.status(HttpStatus.NOT_FOUND).body(error);
}
Así puedes hacer que tu API hable como un humano, no como una IA enfadada.
Así que ya sabes:
✅ Crea una clase de error bonita
✅ Crea un @RestControllerAdvice
✅ Lanza excepciones sin miedo
✅ Deja que Spring se encargue de redirigirlas
✅ Y devuelve respuestas claras, con formato y sentido
P.D.: ¿Tienes ya tu GlobalExceptionHandler
montado o sigues devolviendo errores feos?
O si tienes problemas para montarlo, escríbeme y te lo explico.
Nos leemos en una semana,