JavaRush /Курси /Модуль 5. Spring /Обробка форм у Spring MVC

Обробка форм у Spring MVC

Модуль 5. Spring
Рівень 7 , Лекція 8
Відкрита

Форми — це основний спосіб взаємодії користувача із сервером через веб-інтерфейс. Чи то реєстрація, додавання даних або виконання пошукового запиту, форми є мостом між фронтендом і бэкендом. Spring MVC надає зручні інструменти для обробки даних користувача через форми, їх валідації та повернення назад у представлення, якщо щось пішло не так.


Обробка форм: покроковий процес

Щоб краще зрозуміти, як обробляються форми в Spring MVC, розіб'ємо весь процес на основні кроки:

  1. Створення форми в HTML з використанням Thymeleaf.
  2. Налаштування моделі для передачі даних.
  3. Створення контролера для обробки даних форми.
  4. Валідація даних за допомогою анотації @Valid.
  5. Реакція на помилки і відправка даних назад у форму.

Створимо форму: 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";
    }
}
  1. @GetMapping("/register"): показує сторінку з формою. Ми додаємо порожній об'єкт User в модель, щоб Thymeleaf міг з ним працювати.
  2. @PostMapping("/register"): обробляє POST-запит. Зверни увагу на анотацію @Valid перед параметром User. Це каже Spring, що потрібно виконати валідацію цього об'єкта.
  3. 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. Інакше дані не будуть зв'язані коректно.


Що далі?

Тепер ти вмієш обробляти форми, зв'язувати їх з об'єктами і валідовувати дані. Ці знання пригодяться тобі для створення функціональних інтерфейсів реєстрації, авторизації, створення або редагування сутностей у твоєму застосунку.

Коментарі
ЩОБ ПОДИВИТИСЯ ВСІ КОМЕНТАРІ АБО ЗАЛИШИТИ КОМЕНТАР,
ПЕРЕЙДІТЬ В ПОВНУ ВЕРСІЮ