Implementación de validación de formularios en Spring Boot con Bean Validation

En aplicaciones Spring Boot, la validación de datos de entrada es una tarea común. Spring Boot facilita la integración con Bean Validation (JSR-380) y su implementación Hibernate Validator. Veamos un ejemplo práctico para validar los datos de un formulario que registra nuevos cursos.

Primero, definimos la entidad JPA que representa un curso, aplicando las restricciones de validación directamente sobre los campos.

package com.academico.entidad;

import javax.persistence.*;
import javax.validation.constraints.*;
import java.time.LocalDate;

@Entity
@Table(name = "curso")
public class Curso {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long identificador;

    @NotBlank(message = "El nombre del curso es obligatorio")
    @Size(max = 100, message = "El nombre no puede exceder los 100 caracteres")
    private String nombre;

    @NotNull(message = "La fecha de inicio es obligatoria")
    @FutureOrPresent(message = "La fecha debe ser hoy o en el futuro")
    private LocalDate fechaInicio;

    @Min(value = 1, message = "El número mínimo de participantes es 1")
    @Max(value = 50, message = "El número máximo de participantes es 50")
    private int maxParticipantes;

    @Email(message = "El formato del correo del instructor no es válido")
    private String correoInstructor;

    // Constructores, getters y setters...
    public Curso() {}

    public Long getIdentificador() { return identificador; }
    public void setIdentificador(Long identificador) { this.identificador = identificador; }
    public String getNombre() { return nombre; }
    public void setNombre(String nombre) { this.nombre = nombre; }
    public LocalDate getFechaInicio() { return fechaInicio; }
    public void setFechaInicio(LocalDate fechaInicio) { this.fechaInicio = fechaInicio; }
    public int getMaxParticipantes() { return maxParticipantes; }
    public void setMaxParticipantes(int maxParticipantes) { this.maxParticipantes = maxParticipantes; }
    public String getCorreoInstructor() { return correoInstructor; }
    public void setCorreoInstructor(String correoInstructor) { this.correoInstructor = correoInstructor; }
}

Para el acceso a datos, utilizamos un repositorio de Spring Data JPA.

package com.academico.repositorio;

import com.academico.entidad.Curso;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

@Repository
public interface RepositorioCurso extends JpaRepository<Curso, Long> {
    // Métodos de consulta personalizados se pueden definir aquí
}

El servicio contiane la lógica de negocio y delega la persistencia al repositorio.

package com.academico.servicio;

import com.academico.entidad.Curso;
import com.academico.repositorio.RepositorioCurso;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class ServicioCurso {

    private final RepositorioCurso repositorio;

    public ServicioCurso(RepositorioCurso repositorio) {
        this.repositorio = repositorio;
    }

    @Transactional
    public Curso registrarCurso(Curso curso) {
        // Lógica de negocio adicional puede ir aquí
        return repositorio.save(curso);
    }
}

El controlador REST gestiona las solicitudes HTTP y activa la validación usando la anotación @Valid. El objeto BindingResult captura los errores de validación.

package com.academico.controlador;

import com.academico.entidad.Curso;
import com.academico.servicio.ServicioCurso;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.*;

import javax.validation.Valid;
import java.util.HashMap;
import java.util.Map;
import java.util.stream.Collectors;

@RestController
@RequestMapping("/api/cursos")
public class ControladorCurso {

    private final ServicioCurso servicio;

    public ControladorCurso(ServicioCurso servicio) {
        this.servicio = servicio;
    }

    @PostMapping
    public ResponseEntity<?> crearCurso(@Valid @RequestBody Curso curso, BindingResult resultado) {
        if (resultado.hasErrors()) {
            Map<String, String> errores = new HashMap<>();
            resultado.getFieldErrors().forEach(error ->
                errores.put(error.getField(), error.getDefaultMessage())
            );
            return ResponseEntity.badRequest().body(errores);
        }
        Curso cursoGuardado = servicio.registrarCurso(curso);
        return ResponseEntity.status(HttpStatus.CREATED).body(cursoGuardado);
    }
}

Para probar el endpoint, se puede enviar una solicitud POST con un cuerpo JSON a /api/cursos. Si los datos no cumplen las restricciones, la API devolverá un mensaje de error detallado.

A continuación, se muestra una lista de anotaciones de validación comunes disponibles en Bean Validation:

  • @NotNull: El valor no debe ser nulo.
  • @NotBlank: Para Strings, no debe ser nulo y debe contener al menos un carácter no blanco.
  • @NotEmpty: Para colecciones/arrays/mapas o Strings, no debe ser nulo ni vacío.
  • @Size(min, max): El tamaño de la colección o la longitud del String deben estar dentro de un rango.
  • @Min(value) / @Max(value): Restricciones numéricas.
  • @Email: Debe ser una dirección de correo electrónico válida.
  • @Pattern(regexp): Debe coincidir con la expresión regular proporcionada.
  • @Future / @FutureOrPresent: La fecha debe ser en el futuro o hoy.
  • @Past / @PastOrPresent: La fecha debe ser en el pasado o hoy.
  • @Positive / @PositiveOrZero: El número debe ser positivo (o cero).
  • @Negative / @NegativeOrZero: El número debe ser negativo (o cero).
  • @Digits(integer, fraction): El número debe tener un número específico de dígitos enteros y fraccionarios.

Etiquetas: Spring Boot Bean Validation Hibernate Validator Validación de Formularios Spring Data JPA

Publicado el 7-12 23:22