Validación de datos
Cuando creamos APIs es muy importante validar los datos que envían los usuarios.
Si no validamos los datos pueden ocurrir problemas como:
- datos vacíos
- emails incorrectos
- números negativos
- información inválida en la base de datos
Spring Boot permite validar datos fácilmente usando Jakarta Validation.
1. Añadir dependencia de validación #
En pom.xml añadimos:
1<dependency> 2 <groupId>org.springframework.boot</groupId> 3 <artifactId>spring-boot-starter-validation</artifactId> 4</dependency>
Esto habilita las anotaciones de validación.
2. Validar usando anotaciones #
Spring permite validar los campos usando anotaciones.
Ejemplo:
1public class UserDTO { 2 3 @NotBlank 4 private String name; 5 6 @Email 7 private String email; 8 9}
3. Qué significa #
| anotación | función |
|---|---|
@NotNull | no puede ser null |
@NotBlank | no puede estar vacío |
@Email | debe ser email válido |
@Size | tamaño mínimo/máximo |
@Min | valor mínimo |
@Max | valor máximo |
4. Ejemplo completo DTO validado #
1import jakarta.validation.constraints.Email; 2import jakarta.validation.constraints.NotBlank; 3import jakarta.validation.constraints.Size; 4 5public class UserDTO { 6 7 @NotBlank(message = "El nombre es obligatorio") 8 private String name; 9 10 @Email(message = "Email inválido") 11 private String email; 12 13 @Size(min = 6, message = "La contraseña debe tener al menos 6 caracteres") 14 private String password; 15 16}
5. Activar validación en el Controller #
Para que Spring valide los datos debemos usar:
1@Valid
6. Ejemplo #
1@PostMapping("/users") 2public String createUser(@Valid @RequestBody UserDTO user) { 3 4 return "Usuario creado"; 5 6}
7. Ejemplo petición correcta #
Petición:
1{ 2 "name": "Ana", 3 "email": "ana@email.com", 4 "password": "123456" 5}
Resultado:
1Usuario creado
8. Ejemplo petición incorrecta #
Petición:
1{ 2 "name": "", 3 "email": "correo_invalido", 4 "password": "123" 5}
Respuesta automática de Spring:
1{ 2 "errors": [ 3 "El nombre es obligatorio", 4 "Email inválido", 5 "La contraseña debe tener al menos 6 caracteres" 6 ] 7}
9. Anotaciones de validación más usadas #
@NotNull #
No permite valores nulos.
1@NotNull 2private Integer age;
@NotBlank #
No permite texto vacío.
1@NotBlank 2private String name;
@Email #
Valida emails.
1@Email 2private String email;
@Size #
Define tamaño mínimo y máximo.
1@Size(min = 3, max = 20) 2private String username;
@Min y @Max #
Validan números.
1@Min(18) 2private int age;
10. Validar parámetros de URL #
También podemos validar parámetros.
11. Ejemplo #
1@GetMapping("/users/{id}") 2public String getUser(@PathVariable @Min(1) Long id) { 3 4 return "Usuario " + id; 5 6}
Si enviamos:
1/users/0
Spring devolverá error.
12. Manejo global de errores de validación #
Por defecto Spring devuelve errores simples.
Podemos mejorar la respuesta usando:
1@ControllerAdvice
13. Ejemplo #
1@ControllerAdvice 2public class ValidationHandler { 3 4 @ExceptionHandler(MethodArgumentNotValidException.class) 5 public ResponseEntity<Map<String, String>> handleValidationErrors( 6 MethodArgumentNotValidException ex) { 7 8 Map<String, String> errors = new HashMap<>(); 9 10 ex.getBindingResult().getFieldErrors() 11 .forEach(error -> errors.put( 12 error.getField(), 13 error.getDefaultMessage() 14 )); 15 16 return ResponseEntity.badRequest().body(errors); 17 18 } 19 20}
14. Ejemplo respuesta mejorada #
Si hay errores:
1{ 2 "name": "El nombre es obligatorio", 3 "email": "Email inválido", 4 "password": "La contraseña debe tener al menos 6 caracteres" 5}
Esto es mucho más claro para el cliente.
15. Validaciones personalizadas #
También podemos crear nuestras propias validaciones.
Ejemplo:
1@Target({ElementType.FIELD}) 2@Retention(RetentionPolicy.RUNTIME) 3@Constraint(validatedBy = PhoneValidator.class) 4public @interface ValidPhone { 5 6 String message() default "Teléfono inválido"; 7 8}
Y crear el validador:
1public class PhoneValidator implements ConstraintValidator<ValidPhone, String> { 2 3 public boolean isValid(String phone, ConstraintValidatorContext context) { 4 5 return phone != null && phone.matches("[0-9]{9}"); 6 7 } 8 9}
16. Buenas prácticas #
Validar siempre en DTO #
No validar en entidades.
Usar mensajes claros #
Ejemplo:
1@NotBlank(message="El nombre es obligatorio")
No confiar en el cliente #
Aunque el frontend valide, el backend también debe hacerlo.
Resumen del tema
Conceptos clave #
- Jakarta Bean Validation (
spring-boot-starter-validation): especificación estándar para validación declarativa basada en restricciones de campos. - Anotaciones de Restricción Comunes:
@NotNull(no nulo),@NotBlank(no nulo ni espacios en blanco),@NotEmpty(colecciones o cadenas no vacías),@Email(formato correo),@Size(min, max)y@Min/@Max(rangos numéricos). - Activación en Controladores: anotación
@Valid(o@Validatedpara validación a nivel de parámetros individuales en la clase). - Manejo Centralizado de Errores (
@RestControllerAdvice): captura deMethodArgumentNotValidExceptionpara extraerFieldErrors y formatear respuestas 400 Bad Request limpias y comprensibles para el cliente. - Validadores Personalizados: creación de anotaciones
@ConstraintimplementandoConstraintValidator<A, T>.
Qué debes recordar #
Valida siempre las entradas HTTP en los DTOs usando anotaciones de Jakarta Validation con @Valid y unifica las respuestas de error en un manejador @ControllerAdvice.