CodeGym /Cursos /Módulo 5. Spring /Procesamiento de formularios en Spring MVC

Procesamiento de formularios en Spring MVC

Módulo 5. Spring
Nivel 7 , Lección 8
Disponible

Los formularios son la forma principal en que el usuario interactúa con el servidor a través de la interfaz web. Ya sea para registrarse, añadir datos o ejecutar una búsqueda, los formularios son el puente entre el frontend y el backend. Spring MVC ofrece herramientas cómodas para procesar los datos de usuario a través de formularios, validarlos y devolverlos a las vistas si algo sale mal.


Procesamiento de formularios: proceso paso a paso

Para entender mejor cómo se procesan los formularios en Spring MVC, dividamos todo el proceso en pasos principales:

  1. Crear el formulario en HTML usando Thymeleaf.
  2. Configurar el modelo para pasar los datos.
  3. Crear el controlador para procesar los datos del formulario.
  4. Validación de datos con la anotación @Valid.
  5. Reacción ante errores y envío de datos de vuelta al formulario.

Creemos el formulario: HTML + Thymeleaf

Empecemos con un ejemplo sencillo — registro de usuario. Aquí está el fragmento HTML del formulario:


<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
  <title>Registro</title>
</head>
<body>
  <h1>Registro</h1>
  <form th:action="@{/register}" method="post" th:object="${user}">
    <label for="name">Nombre:</label>
    <input type="text" id="name" th:field="*{name}" />
    <br/>
    <label for="email">Email:</label>
    <input type="email" id="email" th:field="*{email}" />
    <br/>
    <label for="password">Contraseña:</label>
    <input type="password" id="password" th:field="*{password}" />
    <br/>
    <button type="submit">Registrarse</button>
  </form>
</body>
</html>

Desglosamos los elementos principales:

  • th:action="@{/register}" — URL a la que se enviará el formulario. Aquí referenciamos el controlador con la ruta /register.
  • th:object="${user}" — el objeto asociado a este formulario (modelo). En nuestro caso es el objeto user.
  • th:field="*{name}" — vincula el campo del formulario con la propiedad user.name.

El formulario creará una petición HTTP POST con los datos introducidos por el usuario.


Modelo de datos: creación de la clase DTO

Ahora vamos a crear el objeto User para enlazar los datos que vienen del formulario. Es una clase POJO con los campos necesarios:


package com.example.demo.dto;

import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.Size;

public class User {

    @NotBlank(message = "El nombre no debe estar vacío")
    @Size(min = 2, max = 30, message = "El nombre debe tener entre 2 y 30 caracteres")
    private String name;

    @NotBlank(message = "El email no debe estar vacío")
    @Email(message = "Introduce un email válido")
    private String email;

    @NotBlank(message = "La contraseña no debe estar vacía")
    @Size(min = 6, message = "La contraseña debe tener al menos 6 caracteres")
    private String password;

    // Getters y Setters
    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPassword() {
        return password;
    }

    public void setPassword(String password) {
        this.password = password;
    }
}

Puntos interesantes:

  • Las anotaciones del paquete jakarta.validation.constraints nos ayudarán a validar los campos. Por ejemplo, @Email comprobará la corrección del email, y @Size limitará la longitud del texto.

Controlador para procesar los datos

Ahora hagamos el controlador que mostrará la página con el formulario y procesará los datos enviados.


package com.example.demo.controller;

import com.example.demo.dto.User;
import jakarta.validation.Valid;
import org.springframework.stereotype.Controller;
import org.springframework.ui.Model;
import org.springframework.validation.BindingResult;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;

@Controller
public class RegistrationController {

    @GetMapping("/register")
    public String showRegistrationForm(Model model) {
        model.addAttribute("user", new User());
        return "register";
    }

    @PostMapping("/register")
    public String processRegistrationForm(@Valid User user, BindingResult result, Model model) {
        if (result.hasErrors()) {
            return "register"; // Devolver al usuario al formulario si hay errores
        }

        // Aquí puedes guardar los datos en la base o enviarlos a otro sitio
        model.addAttribute("message", "¡Registro completado con éxito!");
        return "success";
    }
}
  1. @GetMapping("/register"): muestra la página con el formulario. Añadimos un objeto vacío User al modelo para que Thymeleaf pueda trabajar con él.
  2. @PostMapping("/register"): procesa la petición POST. Fíjate en la anotación @Valid antes del parámetro User. Eso le dice a Spring que debe validar ese objeto.
  3. BindingResult result: guarda los resultados de la validación. Si hay errores, devolvemos al usuario al formulario.

Validación en el servidor

Supongamos que el usuario introdujo algo incorrecto. Necesitamos mostrar los errores en la página:


<form th:action="@{/register}" method="post" th:object="${user}">
  <label for="name">Nombre:</label>
  <input type="text" id="name" th:field="*{name}" />
  <div th:if="${#fields.hasErrors('name')}" th:errors="*{name}"></div>
  <br/>
  <label for="email">Email:</label>
  <input type="email" id="email" th:field="*{email}" />
  <div th:if="${#fields.hasErrors('email')}" th:errors="*{email}"></div>
  <br/>
  <label for="password">Contraseña:</label>
  <input type="password" id="password" th:field="*{password}" />
  <div th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></div>
  <br/>
  <button type="submit">Registrarse</button>
</form>

¿Qué pasa aquí?

Si la validación del objeto User detecta errores, el método #fields.hasErrors() devolverá true para los campos con errores. La anotación th:errors mostrará automáticamente la descripción del error definida en el modelo.


Errores globales del formulario (por ejemplo, coincidencia de contraseñas)

Si necesitas añadir un error general para el formulario, puedes usar BindingResult:


if (!user.getPassword().equals(user.getConfirmPassword())) {
    result.rejectValue("password", "error.user", "Las contraseñas no coinciden");
}

O un error global:


if (user.getEmail().contains("spam")) {
    result.reject("email.spam", "El email no puede contener 'spam'");
}

Pecados comunes y errores típicos

¿Por qué BindingResult siempre debe ir inmediatamente después de @Valid?

Spring procesa la validación y escribe los resultados en el objeto BindingResult. Si cambias el orden de los argumentos del método, esto lanzará un IllegalStateException.

¡Cuidado con los nombres de los campos!

Los nombres de los campos del formulario (por ejemplo, th:field="*{name}") deben coincidir exactamente con los nombres de las propiedades en el objeto User. Si no, los datos no se enlazarán correctamente.


¿Y ahora qué?

Ahora sabes cómo procesar formularios, vincularlos a objetos y validar datos. Estos conocimientos te servirán para crear interfaces de registro, autenticación, y para crear o editar entidades en tu aplicación.

Comentarios
TO VIEW ALL COMMENTS OR TO MAKE A COMMENT,
GO TO FULL VERSION