A diferencia de lo que uno puedo llegar a pensar, Spring boot proporciona diferentes formas de validar los request, que van desde mecanismos simples, hasta complejas estructuras de clases de validación. En este artículo trataremos de explicar los más utilizados y como los puedes implementar en tus proyectos.

Validación de párametros mediante Bean Validator

La primera forma es usando anotaciones de validación en los parámetros del método del controlador. Por ejemplo, si quieres validar que un parámetro de tipo String no sea nulo ni vacío, puedes usar la anotación @NotNull y @Size:

@PostMapping("/api/example")
public ResponseEntity<String> example(@NotNull @Size(min = 1) String param) {
    // Tu código aquí
}

Si hay varios parámetros que necesitan validación, puedes usar la anotación @Valid en el parámetro que contiene las anotaciones de validación. Por ejemplo:

public class ExampleForm {
    @NotNull
    @Size(min = 1)
    private String param1;

    @NotNull
    @Min(1)
    private Integer param2;

    // getters y setters
}

@PostMapping("/api/example")
public ResponseEntity<String> example(@Valid @RequestBody ExampleForm form) {
    // Tu código aquí
}

La anotación @Valid ayuda a que Spring Boot valide el request antes de ejecutar el método, lo cual es de grán ayuda a evitar que incluse se ejecute el método, sin embargo, abrá ocasiones donde será mejor controlar nosotros mismo el momento exacto de la validación, es por ello que podemos implementar el método de esta otra forma:

@PostMapping("/api/example")
public ResponseEntity<String> example(@Valid @RequestBody ExampleForm form) {
    ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
	Validator validator = factory.getValidator();


	Set<ConstraintViolation<ExampleForm>> violations = validator.validate(form);
	if (!violations.isEmpty()) {
    	// Se encontraron violaciones, procesa las violaciones aquí
	}
}

Bean Validation proporciona un conjunto de anotaciones que puedes usar para validar los atributos de una clase. Algunas de las anotaciones más comunes son:

  • @NotNull: indica que el atributo no puede ser nulo.
  • @Size: indica el tamaño mínimo y/o máximo permitido para un atributo de tipo String o Collection.
  • @Min y @Max: indican el valor mínimo y/o máximo permitido para un atributo numérico.
  • @Email: indica que el atributo debe ser una dirección de correo electrónico válida.
  • @Past y @Future: indican que un atributo de tipo Date o Calendar debe ser una fecha en el pasado o en el futuro, respectivamente.
  • @Pattern: indica un patrón que debe cumplir un atributo de tipo String.
  • @DecimalMin y @DecimalMax: indican el valor mínimo y/o máximo permitido para un atributo de tipo BigDecimal.
  • @NotEmpty: indica que un atributo de tipo String, Collection o Map no puede ser vacío.

Por ejemplo, si tienes una clase que representa un formulario de búsqueda de libros, puedes usar las anotaciones de validación para validar que el título del libro no sea nulo ni vacío, que el precio mínimo y máximo sean números válidos y que la fecha de publicación sea una fecha en el pasado:

public class BookSearchForm {
    @NotNull
    @NotEmpty
    private String title;

    @DecimalMin("0.00")
    @DecimalMax("9999.99")
    private BigDecimal priceMin;

    @DecimalMin("0.00")
    @DecimalMax("9999.99")
    private BigDecimal priceMax;

    @Past
    private Date publicationDate;

    // getters y setters
}

Toma en cuenta que el API de Bean Validator no es parte de Spring Boot, por lo que será necesario agregar la librerías a tu archivo POM.

<dependency>
    <groupId>javax.validation</groupId>
    <artifactId>validation-api</artifactId>
    <version>2.0.1.Final</version>
</dependency>

Clases de tipo validador

Una forma es creando una clase de tipo Validator y registrándola como un bean en la aplicación. La clase Validator tiene un método "supports" que indica qué tipo de objetos puede validar y un método "validate" que recibe el objeto a validar y un objeto de tipo Errors, en el que se pueden agregar los errores de validación encontrados. Por ejemplo:

public class ExampleFormValidator implements Validator {

    @Override
    public boolean supports(Class<?> clazz) {
        return ExampleForm.class.equals(clazz);
    }

    @Override
    public void validate(Object target, Errors errors) {
        ExampleForm form = (ExampleForm) target;
        if (form.getParam1() == null || form.getParam1().trim().isEmpty()) {
            errors.rejectValue("param1", "field.required", "El campo param1 es requerido");
        }
        if (form.getParam2() == null || form.getParam2() < 1) {
            errors.rejectValue("param2", "field.invalid", "El campo param2 es inválido");
        }
    }
}

Para usar el validador, debes registrarlo como un bean en la aplicación y usar la anotación @InitBinder en el método del controlador para asociar el validador a los parámetros que deben ser validados. Por ejemplo:

@RestController
public class ExampleController {

    private final ExampleFormValidator validator;

    public ExampleController(ExampleFormValidator validator) {
        this.validator = validator;
    }

    @InitBinder("form")
    public void initBinder(WebDataBinder binder) {
        binder.setValidator(validator);
    }

    @PostMapping("/api/example")
    public ResponseEntity<String> example(@Valid @ModelAttribute("form") ExampleForm form) {
        // Tu código aquí
    }
}

Controlar los errores de validación

Cuando se produce un error de validación, Spring Boot lanzará una excepción de tipo MethodArgumentNotValidException. Puedes manejar esta excepción de varias formas, como por ejemplo creando una clase de tipo @ControllerAdvice que maneje la excepción y devuelva una respuesta adecuada al usuario. Por ejemplo:

@ControllerAdvice
public class ValidationErrorHandler {

    @ExceptionHandler(MethodArgumentNotValidException.class)
    public ResponseEntity<String> handleValidationErrors(MethodArgumentNotValidException ex) {
        List<FieldError> fieldErrors = ex.getBindingResult().getFieldErrors();
        // Crea una respuesta adecuada basada en los errores de validación
        return ResponseEntity.badRequest().body(/* Tu respuesta aquí */);
    }
}

Si quieres aprender más sobre el desarrollo de API con Spring Boot, no te puede perder nuestro curso Mastering API REST con Spring Boot, donde aprenderás a crear un API REST desde cero con seguridad (JWT), validadores, DTO y las mejores prácticas de la industria.