Форми — це основний спосіб взаємодії користувача із сервером через веб-інтерфейс. Чи то реєстрація, додавання даних або виконання пошукового запиту, форми є мостом між фронтендом і бэкендом. Spring MVC надає зручні інструменти для обробки даних користувача через форми, їх валідації та повернення назад у представлення, якщо щось пішло не так.
Обробка форм: покроковий процес
Щоб краще зрозуміти, як обробляються форми в Spring MVC, розіб'ємо весь процес на основні кроки:
- Створення форми в HTML з використанням Thymeleaf.
- Налаштування моделі для передачі даних.
- Створення контролера для обробки даних форми.
- Валідація даних за допомогою анотації
@Valid. - Реакція на помилки і відправка даних назад у форму.
Створимо форму: HTML + Thymeleaf
Почнемо з простого прикладу — реєстрація користувача. Ось HTML-фрагмент форми:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<title>Реєстрація</title>
</head>
<body>
<h1>Реєстрація</h1>
<form th:action="@{/register}" method="post" th:object="${user}">
<label for="name">Ім'я:</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">Пароль:</label>
<input type="password" id="password" th:field="*{password}" />
<br/>
<button type="submit">Зареєструватися</button>
</form>
</body>
</html>
Розберемо основні елементи:
th:action="@{/register}"— URL, куди буде відправлена форма. Тут ми посилаємося на контролер з маршрутом/register.th:object="${user}"— об'єкт, пов'язаний з цією формою (модель). У нашому випадку це об'єктuser.th:field="*{name}"— зв'язує поле форми з полем об'єктаuser.name.
Форма створить HTTP POST-запит з даними, введеними користувачем.
Модель даних: створення класу DTO
Тепер створимо об'єкт User для зв'язування даних, що надходять з форми. Це POJO-клас з потрібними полями:
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 = "Ім'я не повинно бути порожнім")
@Size(min = 2, max = 30, message = "Ім'я повинно бути довжиною від 2 до 30 символів")
private String name;
@NotBlank(message = "Email не повинен бути порожнім")
@Email(message = "Введіть коректний email")
private String email;
@NotBlank(message = "Пароль не повинен бути порожнім")
@Size(min = 6, message = "Пароль повинен бути довжиною мінімум 6 символів")
private String password;
// Getters і 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;
}
}
Цікаві моменти:
- Анотації з пакету
jakarta.validation.constraintsдопоможуть нам валідовувати поля. Наприклад,@Emailперевірить коректність email, а@Sizeобмежить довжину тексту.
Контролер для обробки даних
Тепер зробимо контролер, який показуватиме сторінку з формою і оброблятиме відправлені дані.
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"; // Повернути користувача на форму, якщо є помилки
}
// Тут ти можеш зберегти дані в базу або відправити кудись ще
model.addAttribute("message", "Реєстрація пройшла успішно!");
return "success";
}
}
@GetMapping("/register"): показує сторінку з формою. Ми додаємо порожній об'єктUserв модель, щоб Thymeleaf міг з ним працювати.@PostMapping("/register"): обробляє POST-запит. Зверни увагу на анотацію@Validперед параметромUser. Це каже Spring, що потрібно виконати валідацію цього об'єкта.BindingResult result: зберігає результати валідації. Якщо є помилки, ми повертаємо користувача назад на форму.
Валідація на стороні сервера
Припустимо, користувач ввів щось некоректно. Нам потрібно відобразити помилки на сторінці:
<form th:action="@{/register}" method="post" th:object="${user}">
<label for="name">Ім'я:</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">Пароль:</label>
<input type="password" id="password" th:field="*{password}" />
<div th:if="${#fields.hasErrors('password')}" th:errors="*{password}"></div>
<br/>
<button type="submit">Зареєструватися</button>
</form>
Що відбувається?
Якщо валідація об'єкта User виявила помилки, метод #fields.hasErrors() поверне true для полів з помилками. Анотація th:errors автоматично виведе опис помилки, заданий у моделі.
Глобальні помилки форми (наприклад, співпадіння паролів)
Якщо потрібно додати загальну помилку для форми, це можна зробити через BindingResult:
if (!user.getPassword().equals(user.getConfirmPassword())) {
result.rejectValue("password", "error.user", "Паролі не збігаються");
}
Або глобальну помилку:
if (user.getEmail().contains("spam")) {
result.reject("email.spam", "Email не може містити 'spam'");
}
Підводні камені і типові помилки
Чому BindingResult завжди повинен бути одразу після @Valid?
Spring обробляє валідацію і записує результати в об'єкт BindingResult. Якщо ти поміняєш порядок аргументів методу, це викличе IllegalStateException.
Обережно з назвами полів!
Імена полів форми (наприклад, th:field="*{name}") повинні точно співпадати з іменами полів в об'єкті User. Інакше дані не будуть зв'язані коректно.
Що далі?
Тепер ти вмієш обробляти форми, зв'язувати їх з об'єктами і валідовувати дані. Ці знання пригодяться тобі для створення функціональних інтерфейсів реєстрації, авторизації, створення або редагування сутностей у твоєму застосунку.
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ