Encodera

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.

Оглавление

Формы

Через API статьи создают программы, а людям нужна обычная HTML-форма. Thymeleaf связывает поля формы с объектом Java: показывает в полях его значения, а после отправки Spring собирает объект обратно из полей. Проверка @Valid работает так же, как в API, только ошибки выводятся прямо в форме.

Показ формы

// src/main/java/com/example/blog/PageController.java
@GetMapping("/posts/new")
public String newPost(Model model) {
    model.addAttribute("post", new NewPostRequest("", "", null));
    model.addAttribute("users", users.findAll());
    return "posts/form";
}

В модель кладём пустую запись NewPostRequest — форма заполнится её значениями, и список пользователей для выбора автора. UserRepository users добавьте в конструктор PageController.

Шаблон формы

<!-- src/main/resources/templates/posts/form.html -->
<form th:action="@{/posts}" th:object="${post}" method="post">
    <label>Заголовок
        <input type="text" th:field="*{title}">
    </label>
    <p class="error" th:if="${#fields.hasErrors('title')}" th:errors="*{title}">Ошибка</p>
 
    <label>Текст
        <textarea th:field="*{body}" rows="8"></textarea>
    </label>
    <p class="error" th:if="${#fields.hasErrors('body')}" th:errors="*{body}">Ошибка</p>
 
    <label>Автор
        <select th:field="*{authorId}">
            <option th:each="user : ${users}" th:value="${user.id}" th:text="${user.username}">anna</option>
        </select>
    </label>
 
    <button type="submit">Опубликовать</button>
</form>

th:object="${post}" выбирает объект формы, а th:field="*{title}" связывает поле с его компонентом: Thymeleaf сам поставит id, name и value. th:errors выводит сообщения об ошибках поля. Шаблону хватает компонентов записи — геттеры не нужны.

Обработка отправки

// src/main/java/com/example/blog/PageController.java
@PostMapping("/posts")
public String create(@Valid @ModelAttribute("post") NewPostRequest form,
                     BindingResult result, Model model) {
    if (result.hasErrors()) {
        model.addAttribute("users", users.findAll());
        return "posts/form";
    }
    PostResponse post = postService.create(form);
    return "redirect:/posts/" + post.id();
}

@ModelAttribute("post") собирает запись из полей формы. BindingResult хранит результат проверки: при ошибках мы снова показываем форму — введённые значения и сообщения окажутся на месте. При успехе возвращаем redirect: — браузер перейдёт на страницу новой статьи. Так повторное обновление страницы не отправит форму второй раз.

Проверка

Откройте http://localhost:8080/posts/new и нажмите «Опубликовать» с пустыми полями — под полями появится «не должно быть пустым». То же видно через curl:

curl -i -X POST http://localhost:8080/posts -d "title=Из формы&body=Написано в браузере&authorId=2"
HTTP/1.1 302
Location: http://localhost:8080/posts/2

Код 302 и заголовок Location — это и есть переадресация после успешной отправки.

Частая ошибка

BindingResult должен идти в параметрах сразу после проверяемого объекта. Если поставить между ними другой параметр, например Model, Spring решит, что обрабатывать ошибки вы не собираетесь: метод не вызовется, а пользователь вместо формы с подсказками увидит страницу ошибки 400.

Спасибо, что читаете!

Пожертвуйте сайту, мы стараемся ради вас.