CodeGym /Cursos /Módulo 5. Spring /Manejo global de errores con @ControllerAdvice

Manejo global de errores con @ControllerAdvice

Módulo 5. Spring
Nivel 8 , Lección 5
Disponible

Vamos a dar un paso adelante y nos sumergimos en el manejo global de errores con la herramienta potente de Spring — @ControllerAdvice. En esta lección vas a aprender cómo centralizar el manejo de todos los errores de la aplicación, hacerlo más flexible y más limpio.


¿Qué es @ControllerAdvice?

@ControllerAdvice — es una anotación del Spring Framework que te permite gestionar de forma centralizada el manejo de excepciones para todos los controllers de tu aplicación. Si antes podías manejar excepciones localmente a nivel de un solo controller con @ExceptionHandler, con @ControllerAdvice obtienes la posibilidad de sacar la lógica de manejo de errores a una clase separada que se aplicará a todos los controllers.

Imagina que tienes una aplicación con muchos controllers, donde diferentes endpoints pueden lanzar las mismas excepciones (por ejemplo, MethodArgumentNotValidException en errores de validación). Si escribes handlers en cada controller, el código se va a duplicar y será difícil de mantener. @ControllerAdvice resuelve ese problema: declarando una vez un handler global, puedes unificar la lógica de manejo de errores.

¿Cómo funciona?

Spring registra automáticamente las clases con la anotación @ControllerAdvice y las aplica a todos los controllers de tu aplicación. Puedes combinar esto con la anotación @ExceptionHandler para definir qué excepciones manejar.


¿Cómo configurar un handler global usando @ControllerAdvice?

Paso 1. Crear la clase con la anotación @ControllerAdvice

Creamos una nueva clase que manejará las excepciones de forma global. Por ejemplo, la llamaremos GlobalExceptionHandler.


package com.example.demo.exception;

import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;

@ControllerAdvice
public class GlobalExceptionHandler {

    // Manejo de excepción genérica
    @ExceptionHandler(Exception.class)
    public ResponseEntity<String> handleGenericException(Exception ex) {
        return new ResponseEntity<>("Ocurrió un error: " + ex.getMessage(), HttpStatus.INTERNAL_SERVER_ERROR);
    }
}

¿Qué pasa aquí?

  • Usamos @ControllerAdvice para indicar a Spring que esta clase manejará las excepciones para todos los controllers.
  • En el método handleGenericException capturamos cualquier excepción (Exception.class) y devolvemos un mensaje simple de error con el estado HTTP 500 Internal Server Error.

Paso 2. Manejar excepciones concretas

A menudo no queremos manejar todas las excepciones por igual, sino tipos concretos, por ejemplo errores de validación o situaciones en las que no se encuentra un recurso. Añadamos handlers para esos casos.


import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.http.HttpStatus;

@ControllerAdvice
public class GlobalExceptionHandler {

    // Manejo de errores de validación
    @ExceptionHandler(MethodArgumentNotValidException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public ResponseEntity<String> handleValidationException(MethodArgumentNotValidException ex) {
        return new ResponseEntity<>("Error de validación: revisa los datos enviados", HttpStatus.BAD_REQUEST);
    }

    // Manejo de recurso no encontrado
    @ExceptionHandler(ResourceNotFoundException.class)
    @ResponseStatus(HttpStatus.NOT_FOUND)
    public ResponseEntity<String> handleResourceNotFoundException(ResourceNotFoundException ex) {
        return new ResponseEntity<>(ex.getMessage(), HttpStatus.NOT_FOUND);
    }
}

Ten en cuenta:

  • Para manejar errores de validación hemos añadido un handler que captura MethodArgumentNotValidException. Esta excepción se genera automáticamente al usar @Valid.
  • Para errores de recurso asumimos que tenemos una excepción propia ResourceNotFoundException. Es un ejemplo de excepción personalizada que puede lanzarse desde la capa de servicio.

Paso 3. Crear una excepción personalizada

Si tienes situaciones que requieren tu propia lógica, es mejor crear una excepción personalizada. Aquí tienes un ejemplo:


package com.example.demo.exception;

public class ResourceNotFoundException extends RuntimeException {
    public ResourceNotFoundException(String message) {
        super(message);
    }
}

Ahora puedes lanzar esta excepción, por ejemplo, en el servicio:


if (resource == null) {
    throw new ResourceNotFoundException("Recurso con ID " + id + " no encontrado");
}

Ejemplo: Manejo conjunto de errores

Juntamos todo. Supongamos que tenemos un controller simple con un método para obtener un recurso por ID. Si no se encuentra el recurso, lanzamos ResourceNotFoundException. Si los datos son incorrectos, Spring lanzará automáticamente MethodArgumentNotValidException.

Controller


package com.example.demo.controller;

import com.example.demo.exception.ResourceNotFoundException;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import javax.validation.constraints.Min;

@RestController
@RequestMapping("/api/resources")
public class ResourceController {

    @GetMapping("/{id}")
    public String getResource(@PathVariable @Min(1) Long id) {
        // Simulación de búsqueda de recurso
        if (id == 999) {
            throw new ResourceNotFoundException("Recurso con ID " + id + " no encontrado");
        }
        return "Recurso encontrado: ID = " + id;
    }
}

Detalles:

  • Aplicamos la anotación @Min(1) para validar el ID. Si el ID es menor que 1, Spring lanzará automáticamente MethodArgumentNotValidException.
  • Si el recurso con ID 999 no existe, lanzamos nuestra excepción personalizada.

Resultados

  1. Si el ID es menor que 1:
    
    HTTP 400 BAD REQUEST
    Error de validación: revisa los datos enviados
    
  2. Si el recurso no se encuentra (ID = 999):
    
    HTTP 404 NOT FOUND
    Recurso con ID 999 no encontrado
    
  3. Si ocurre cualquier otra excepción:

    
    HTTP 500 INTERNAL SERVER ERROR
    Ocurrió un error: [texto de la excepción]
    

Ventajas de usar @ControllerAdvice

  1. Centralización del manejo de errores: todo el código de manejo de errores está en un solo sitio, lo que facilita el mantenimiento de la aplicación.
  2. Uniformidad: todos los errores se manejan de la misma forma, lo que hace el API más predecible y cómodo para los clientes.
  3. Flexibilidad: puedes añadir handlers para distintos tipos de excepciones y configurarlos según tus necesidades.

Mejorando la experiencia del usuario

El manejo global de errores no es solo para la comodidad del desarrollador, sino también para la usabilidad de tu API o aplicación web. Puedes hacer los mensajes de error más informativos, localizarlos para soportar varios idiomas y registrarlos (log) para analizar y solucionar problemas.

En las siguientes lecciones tocaremos estos aspectos: cómo formar mensajes de error personalizados, cómo localizarlos y cómo implementar logging efectivo.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION