Spring Boot – Manejo de excepciones

Spring Boot está construido en la parte superior del resorte y contiene todas las características del resorte. Y se está convirtiendo en el favorito de los desarrolladores en estos días debido a su rápido entorno listo para producción que les permite concentrarse directamente en la lógica en lugar de luchar con la configuración y la instalación. Spring Boot es un marco basado en microservicios y crear una aplicación lista para producción lleva muy poco tiempo. El manejo de excepciones en Spring Boot ayuda a lidiar con errores y excepciones presentes en las API para ofrecer una aplicación empresarial robusta. Este artículo cubre varias formas en que se pueden manejar las excepciones en un proyecto Spring Boot. Hagamos la configuración inicial para explorar cada enfoque con más profundidad.

Configuración inicial

Para crear un proyecto Spring Boot simple con Spring Initializer, consulte este artículo . Ahora, desarrollemos un servicio web Spring Boot Restful que realice operaciones CRUD en la entidad del cliente . Usaremos la base de datos MYSQL para almacenar todos los datos necesarios.

Paso 1: crear un cliente de clase de entidad JPA con tres campos de identificación, nombre y dirección.

Java

// Step 1: Creating a JPA Entity
// class Customer with three
// fields id, name and address.
package com.customer.model;
 
import javax.persistence.Entity;
import javax.persistence.GeneratedValue;
import javax.persistence.GenerationType;
import javax.persistence.Id;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
 
@Entity
@Data
@AllArgsConstructor
@NoArgsConstructor
public class Customer {
 
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private String name;
    private String address;
}

La clase Cliente se anota con la anotación @Entity y define captadores, establecedores y constructores para los campos.

Paso 2: Creación de una interfaz CustomerRepository

Java

// Step 2: Creating a Repository
// Interface extending
// JpaRepository
package com.customer.repository;
 
import com.customer.model.Customer;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
 
@Repository
public interface CustomerRepository
    extends JpaRepository<Customer, Long> {
}

La interfaz CustomerRepository está anotada con la anotación @Repository y amplía el JpaRepository de Spring Data JPA .

Paso 3: creación de excepciones personalizadas que se pueden lanzar durante los escenarios necesarios mientras se realiza CRUD.

CustomerAlreadyExistsException: esta excepción se puede generar cuando el usuario intenta agregar un cliente que ya existe en la base de datos.

Java

// Step 3: Creating custom exception
// that can be thrown when
// user tries to add a customer
// that already exists
package com.customer.exception;
 
public class CustomerAlreadyExistsException
    extends RuntimeException {
 
    private String message;
 
    public CustomerAlreadyExistsException() {}
 
    public CustomerAlreadyExistsException(String msg)
    {
        super(msg);
        this.message = msg;
    }
}

NoSuchCustomerExistsException: esta excepción se puede generar cuando el usuario intenta eliminar o actualizar un registro de cliente que no existe en la base de datos.

Java

// Step 3: Creating custom exception
// that can be thrown when
// user tries to update/delete a
// customer that doesn't exists
package com.customer.exception;
 
public class NoSuchCustomerExistsException
    extends RuntimeException {
 
    private String message;
 
    public NoSuchCustomerExistsException() {}
 
    public NoSuchCustomerExistsException(String msg)
    {
        super(msg);
        this.message = msg;
    }
}

Nota : ambas clases de excepciones personalizadas amplían RuntimeException.

Paso 4: Creación de la interfaz CustomerService e implementación de la clase CustomerServiceImpl de la capa de servicio.

La interfaz CustomerService define tres métodos diferentes:

  1. Customer getCustomer(Long id): Para obtener un registro de cliente por su id. Este método lanza una excepción NoSuchElementException cuando no encuentra un registro de cliente con la identificación dada.
  2. String addCustomer(Cliente cliente): Para agregar detalles de un nuevo Cliente a la base de datos. Este método genera una excepción CustomerAlreadyExistsException cuando el usuario intenta agregar un cliente que ya existe.
  3. String updateCustomer (cliente cliente): para actualizar los detalles de los clientes ya existentes. Este método genera una excepción NoSuchCustomerExistsException cuando el usuario intenta actualizar los detalles de un cliente que no existe en la base de datos.

La clase de implementación de interfaz y servicio es la siguiente:

Java

// Step 4: Creating service interface
package com.customer.service;
import com.customer.model.Customer;
 
public interface CustomerService {
 
    // Method to get customer by its Id
    Customer getCustomer(Long id);
 
    // Method to add a new Customer
    // into the database
    String addCustomer(Customer customer);
 
    // Method to update details of a Customer
    String updateCustomer(Customer customer);
}

Java

// Step 4: Service class Implementation
// which defines the main logic
package com.customer.service;
 
// Importing required packages
import com.customer.exception.CustomerAlreadyExistsException;
import com.customer.exception.NoSuchCustomerExistsException;
import com.customer.model.Customer;
import com.customer.repository.CustomerRepository;
import java.util.NoSuchElementException;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
 
@Service
public class CustomerServiceImpl
    implements CustomerService {
 
    @Autowired
    private CustomerRepository customerRespository;
 
    // Method to get customer by Id.Throws
    // NoSuchElementException for invalid Id
    public Customer getCustomer(Long id)
    {
        return customerRespository.findById(id).orElseThrow(
            ()
                -> new NoSuchElementException(
                    "NO CUSTOMER PRESENT WITH ID = " + id));
    }
 
    // Method to add new customer details to database.Throws
    // CustomerAlreadyExistsException when customer detail
    // already exist
    public String addCustomer(Customer customer)
    {
        Customer existingCustomer
            = customerRespository.findById(customer.getId())
                  .orElse(null);
        if (existingCustomer == null) {
            customerRespository.save(customer);
            return "Customer added successfully";
        }
        else
            throw new CustomerAlreadyExistsException(
                "Customer already exists!!");
    }
 
    // Method to update customer details to database.Throws
    // NoSuchCustomerExistsException when customer doesn't
    // already exist in database
    public String updateCustomer(Customer customer)
    {
        Customer existingCustomer
            = customerRespository.findById(customer.getId())
                  .orElse(null);
        if (existingCustomer == null)
            throw new NoSuchCustomerExistsException(
                "No Such Customer exists!!");
        else {
            existingCustomer.setName(customer.getName());
            existingCustomer.setAddress(
                customer.getAddress());
            customerRespository.save(existingCustomer);
            return "Record updated Successfully";
        }
    }
}

Paso 5: Creación de Rest Controller CustomerController que define varias API.

Java

// Step 5: Creating Rest Controller CustomerController which
// defines various API's.
package com.customer.controller;
 
// Importing required packages
import com.customer.exception.CustomerAlreadyExistsException;
import com.customer.exception.ErrorResponse;
import com.customer.model.Customer;
import com.customer.service.CustomerService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;
 
@RestController
public class CustomerController {
 
    @Autowired private CustomerService customerService;
 
    // Get Customer by Id
    @GetMapping("/getCustomer/{id}")
    public Customer getCustomer(@PathVariable("id") Long id)
    {
        return customerService.getCustomer(id);
    }
 
    // Add new Customer
    @PostMapping("/addCustomer")
    public String
    addcustomer(@RequestBody Customer customer)
    {
        return customerService.addCustomer(customer);
    }
 
    // Update Customer details
    @PutMapping("/updateCustomer")
    public String
    updateCustomer(@RequestBody Customer customer)
    {
        return customerService.updateCustomer(customer);
    }
}

Ahora veamos las diversas formas en que podemos manejar las excepciones lanzadas en este proyecto.

Manejo de excepciones predeterminado por Spring Boot:

El método getCustomer() definido por CustomerController se usa para obtener un cliente con una identificación dada. Lanza una NoSuchElementException cuando no encuentra un registro de Cliente con la identificación dada. Al ejecutar la aplicación Spring Boot y presionar la API /getCustomer con una identificación de cliente no válida, Spring Boot maneja completamente una excepción NoSuchElementException de la siguiente manera:

Spring Boot proporciona una respuesta de error sistemática al usuario con información como la marca de tiempo, el código de estado HTTP, el error, el mensaje y la ruta.

Usando la anotación Spring Boot @ExceptionHandler :

La anotación @ExceptionHandler proporcionada por Spring Boot se puede usar para manejar excepciones en clases de controlador o métodos de controlador particulares. Spring Configuration reconoce automáticamente cualquier método anotado con esto como un método de manejo de excepciones. Un método del controlador de excepciones maneja todas las excepciones y sus subclases pasadas en el argumento. También se puede configurar para devolver una respuesta de error específica al usuario. Así que vamos a crear una clase personalizada ErrorResponse para que la excepción se transmita al usuario de una manera clara y concisa de la siguiente manera:

Java

// Custom Error Response Class
package com.customer.exception;
 
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
 
@Data
@AllArgsConstructor
@NoArgsConstructor
public class ErrorResponse {
 
    private int statusCode;
    private String message;
 
    public ErrorResponse(String message)
    {
        super();
        this.message = message;
    }
}

El método addCustomer() definido por CustomerController arroja una excepción CustomerAlreadyExistsException cuando el usuario intenta agregar un cliente que ya existe en la base de datos; de lo contrario, guarda los detalles del cliente. 

Para manejar esta excepción, definamos un método de controlador handleCustomerAlreadyExistsException() en CustomerController . Así que ahora, cuando addCustomer() lanza una excepción CustomerAlreadyExistsException , se invoca el método de controlador que devuelve una respuesta de error adecuada al usuario.

Java

// Exception Handler method added in CustomerController to
// handle CustomerAlreadyExistsException exception
@ExceptionHandler(value
                  = CustomerAlreadyExistsException.class)
@ResponseStatus(HttpStatus.CONFLICT)
public ErrorResponse
handleCustomerAlreadyExistsException(
    CustomerAlreadyExistsException ex)
{
    return new ErrorResponse(HttpStatus.CONFLICT.value(),
                             ex.getMessage());
}

Nota : Spring Boot permite anotar un método con @ResponseStatus para devolver el código de estado Http requerido.

Al ejecutar la aplicación Spring Boot y presionar la API /addCustomer con un cliente existente, CustomerAlreadyExistsException se maneja completamente mediante el método del controlador de la siguiente manera:

Usando @ControllerAdvice para el controlador de excepciones global:

En el enfoque anterior, podemos ver que el método anotado @ExceptionHandler solo puede manejar las excepciones lanzadas por esa clase en particular. Sin embargo, si queremos manejar cualquier excepción lanzada a lo largo de la aplicación, podemos definir una clase de controlador de excepción global y anotarla con @ControllerAdvice . Esta anotación ayuda a integrar múltiples controladores de excepción en una sola unidad global. 

El método updateCustomer() definido en CustomerController arroja una excepción NoSuchCustomerExistsException si el usuario intenta actualizar los detalles de un cliente que aún no existe en la base de datos; de lo contrario, guarda con éxito los detalles actualizados para ese cliente en particular.

Para manejar esta excepción, definamos una clase GlobalExceptionHandler anotada con @ControllerAdvice . Esta clase define el método ExceptionHandler para la excepción NoSuchCustomerExistsException de la siguiente manera.

Java

// Class to handle exception Globally
package com.customer.exception;
 
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseBody;
import org.springframework.web.bind.annotation.ResponseStatus;
 
@ControllerAdvice
public class GlobalExceptionHandler {
 
    @ExceptionHandler(value
                      = NoSuchCustomerExistsException.class)
    @ResponseStatus(HttpStatus.BAD_REQUEST)
    public @ResponseBody ErrorResponse
    handleException(NoSuchCustomerExistsException ex)
    {
        return new ErrorResponse(
            HttpStatus.NOT_FOUND.value(), ex.getMessage());
    }
}

Al ejecutar la aplicación Spring Boot y presionar la API /updateCustomer con detalles de cliente no válidos, se lanza NoSuchCustomerExistsException , que es completamente manejada por el método del controlador definido en la clase GlobalExceptionHandler de la siguiente manera:

Publicación traducida automáticamente

Artículo escrito por akankshapatro y traducido por Barcelona Geeks. The original can be accessed here. Licence: CCBY-SA

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *