Curso de Spring Boot

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.


8.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.


8.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}

Qué significa

anotaciónfunción
@NotNullno puede ser null
@NotBlankno puede estar vacío
@Emaildebe ser email válido
@Sizetamaño mínimo/máximo
@Minvalor mínimo
@Maxvalor máximo

8.3 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}

8.4 Activar validación en el Controller

Para que Spring valide los datos debemos usar:

1@Valid

Ejemplo

1@PostMapping("/users")
2public String createUser(@Valid @RequestBody UserDTO user) {
3
4    return "Usuario creado";
5
6}

8.5 Ejemplo petición correcta

Petición:

1{
2  "name": "Ana",
3  "email": "ana@email.com",
4  "password": "123456"
5}

Resultado:

1Usuario creado

8.6 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}

8.7 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;

8.8 Validar parámetros de URL

También podemos validar parámetros.


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.


8.9 Manejo global de errores de validación

Por defecto Spring devuelve errores simples.

Podemos mejorar la respuesta usando:

1@ControllerAdvice

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}

8.10 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.


8.11 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}

8.12 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.

  • Loading...