Spring Boot Thymeleaf Form Validación de datos con Bean Validator

En este tutorial, cubriremos cómo realizar la validación de datos de formulario Spring Boot Thymeleaf utilizando la conocida biblioteca Bean Validator.

Introducción

La validación de datos de formulario es un paso muy común y rudimentario en la creación de cualquier aplicación web con la entrada del usuario. Queremos asegurarnos de que se respeten ciertos rangos y que se sigan ciertos formatos. Por ejemplo, queremos asegurarnos de que el usuario no tenga “-345” años o que su dirección de correo electrónico sea válida.

Hay muchas formas de validar los datos del formulario, y el método que utilice depende de su aplicación. En general, querrá realizar la validación del lado del cliente, así como la validación del lado del servidor. La validación del lado del cliente se asegura de que los datos sin filtrar ni siquiera lleguen al back-end, mientras que la validación del lado del servidor se asegura de que los datos incorrectos no se procesen más.

En este artículo, cubriremos cómo realizar la validación de datos de formulario en Spring Boot con Thymeleaf, como motor de plantilla.

Aprovecharemos la [API de validación de frijoles] integrada de Spring Boot (https://beanvalidation.org/), que hace que este proceso sea simple y directo.

Dependencia de Spring Boot Validation Maven

Spring Boot nos permite definir criterios de validación mediante anotaciones. En su modelo de dominio, simplemente puede anotar campos con las restricciones y las aplicará.

Para que las anotaciones de validación funcionen, necesitamos agregar la siguiente dependencia:

1
2
3
4
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

Modelo de dominio

Definamos un POJO simple, con algunos campos que queremos validar:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
@Entity
public class Person {

  @Id
  @GeneratedValue(strategy= GenerationType.AUTO)
  private Long id;

  @NotEmpty
  @Size(min = 5)
  private String fullName;

  @NotEmpty
  @Email
  private String email;

  @NotNull
  @Min(value = 18)
  private Integer age;

  // Getters and Setters

}

Ahora, rompamos las anotaciones que usamos:

  • @NotEmpty - se utiliza para restringir un campo de tipo String, Collection, Map o Array para que no sea nulo o vacío.
  • @Size([min = x, max = y]) - se usa para definir las reglas para el tamaño de una String, Collection, Map o Array.
  • @Email - nos ayuda a validar la cadena contra una expresión regular que define la estructura de un correo electrónico válido.
  • @NotNull - le dice a Spring que el campo no debe ser nulo, pero puede estar vacío.
  • @Min y @Max se utilizan para especificar los límites de una variable. Por ejemplo, la edad @Min podría establecerse en, digamos, 18.

Capa de persistencia - Repositorio

Para crear un repositorio CRUD simple, todo lo que tenemos que hacer es extender el JpaRepository y proporcionar nuestro modelo de dominio y el tipo de datos de ID:

1
2
3
@Repository
public interface PersonRepository extends JpaRepository<Person, Long> {
}

Creación de un formulario con Thymeleaf

Ahora, hagamos un formulario simple usando HTML y Bootstrap para recopilar información:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<form th:action="@{/add}" th:object="${person}" method="post" class="form">
    <div class="form-group">
        <label for="fullName">Name</label>
        <input class="form-control" type="text" th:field="*{fullName}" id="fullName" placeholder="Full Name">
        <div class="alert alert-warning" th:if="${#fields.hasErrors('fullName')}" th:errors="*{fullName}"></div>
    </div>
    <div class="form-group">
        <label for="email">Email</label>
        <input class="form-control" type="text" th:field="*{email}" id="email" placeholder="Email">
        <div class="alert alert-warning" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
    </div>
    <div class="form-group">
        <label for="age">Age</label>
        <input class="form-control" type="text" th:field="*{age}" id="age" placeholder="Age">
        <div class="alert alert-warning" th:if="${#fields.hasErrors('age')}" th:errors="*{age}"></div>
    </div>
    <input type="submit"  class="btn btn-success" value="Add User">
</form>

El formulario apunta a /add y envía una solicitud POST. Los campos en nuestro objeto, fullName, age y email están en la forma, indicada por th:field. Ya que tenemos nuestro th:object=${person}, podemos referirnos a este objeto person sustituyéndolo con un * antes de los campos.

*{fullName} es lo mismo que ${person.fullName}. Cada entrada también tiene un <div> oculto que se muestra solo si las llamadas ${#fields.hasErrors()} se evalúan como true. Si no hay errores, este div no existe. Si los hay, la etiqueta th:errors nos permite especificar un mensaje. Si simplemente pasamos el campo que causa el error, como th:errors="*{age}", se usa el mensaje predeterminado de la API Bean Validator para ese campo.

Esto da como resultado un formulario que se ve así:

basic html form

Dado que estos mensajes no son muy fáciles de usar, queremos personalizarlos proporcionando nuestros propios mensajes.

Controlador

Ahora, hagamos un controlador que manejará una solicitud para guardar una ‘Persona’ en la base de datos. Como de costumbre, tendremos un @GetMapping() para mostrar el formulario y un @PostMapping para manejar la solicitud. En la firma del método @PostMapping, anotaremos el POJO con @Valid.

La anotación @Valid activa el Bean Validator para verificar si los campos poblados en el objeto se ajustan a las anotaciones que hemos usado en la definición de clase. Si no usa la anotación @Valid, no verificará nada, e incluso los valores que no esperaba se pueden completar en el objeto.

En nuestro caso, el objeto Persona persona es el objeto que se completa con las entradas de un formulario:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
@GetMapping("/add")
public String showAddPersonForm(Person person) {
  return "add-person";
}

@PostMapping("/add")
public String addPerson(@Valid Person person, BindingResult result, Model model) {
  if (result.hasErrors()) {
    return "add-person";
  }
  repository.save(person);
  return "redirect:/index";
}

Si hay algún problema con esta Persona, el atributo ${fields} tendrá errores dentro de él, vinculado al campo que causó el error.

Personalización de mensajes de error

Para configurar un mensaje personalizado para cualquier restricción de validación, puede usar la opción mensaje:

1
2
@NotEmpty(message = "Field can't be empty!)
private String field;

Simplemente puede escribir estos mensajes en su modelo, así. Sin embargo, se considera una buena práctica colocar los mensajes de validación en un archivo de “propiedades” al que pueda hacer referencia. Esto se hace porque puede agrupar todos los mensajes de validación y puede actualizarlos si los modelos se modifican en una fecha posterior.

Hagamos nuestros ValidationMessages.properties en src/main/resources:

1
Size.Person.FullName=The Full Name should have at least 5 characters

Luego modificaremos el modelo Persona y proporcionaremos esta propiedad como el mensaje de la anotación @Tamaño:

1
2
3
@NotEmpty(message = "The Full Name can't be null")
@Size(min = 5, message = "{Size.Person.FullName}")
private String fullName;

Volvamos a nuestro formulario y veamos cómo se ve ahora:

customized error messages spring boot bean validator

Conclusión

En este artículo, hemos repasado cómo usar la API Bean Validator que Spring Boot usa para realizar sin esfuerzo la validación de datos de formulario con Thymeleaf.

Hemos creado un modelo de dominio, anotado nuestros campos con restricciones Bean Validator. Luego, creamos un repositorio y un controlador para mostrar el formulario y procesarlo, así como para guardar la información relevante en un objeto Persona, asegurándonos de que los campos estén validados con la anotación @Valid.

Puede encontrar el código fuente en GitHub.