arrow-left

All pages
gitbookPowered by GitBook
1 of 1

Loading...

Лекция 9

hashtag
Тема: Получение данных формы. Валидация данных, Bean Validation API. Загрузка файлов на сервер.

hashtag
6. Валидация данных формы

Создадим проект с индексной страницей, на которой расположена форма добавления нового студента.

Изначально, форма не имеет средств валидации, то есть мы не можем отследить корректность заполнения формы.

Spring предоставляет несколько инструментов для реализации валидации формы, воспользуемся библиотекой Bean Validation API

circle-info

Информацию по поводу использования библиотеки можно найти (см. раздел 8 мануала)

Добавим библиотеку в список зависимостей в файле pom.xml

Будем использовать механизм встроенных ограничений. Алгоритм использования встроенных ограничений следующий - с помощью аннотаций необходимо указать над полем класса-сущности требуемые параметры валидации и другие параметры. В нашем случае, сущностью выступает класс Student. Добавим необходимые ограничения для полей сущности.

Как мы видим, все достаточно просто и наглядно.

Далее, нам необходимо модифицировать контроллеры и реализовать следующий функционал:

  • указать, что объект типа Student должен пройти валидацию;

  • получить результаты валидации объекта;

  • если объект не прошел валидацию - не добавлять объект в хранилище, выдать сообщение об ошибке в консоль.

Нам необходимо модифицировать метод контроллера, который обрабатывает данные формы. Указываем аннотацию @Valid, которая говорит о том, что полученный объект необходимо подвергнуть валидации. Далее указываем аргумент типа BindingResult, который хранит информацию о результате валидации. С помощью метода hasErrors() получаем результат валидации объекта.

При попытке отправить пустую форму, получаем сообщение в консоли

Последний шаг - необходимо предоставить пользователю информацию о том, что то или иное поле формы не прошло валидацию.

Самый простой способ проинформировать пользователь - показать сообщение об ошибке около поля, которое не прошло валидацию. Чтобы реализовать данный функционал, перейдем в шаблон index.html.

Рассмотрим поле "Фамилия". Сообщение об ошибке мы разместим снизу поля. Добавим соответствующий элемент <small> в HTML-макет.

Используем тег th:if. Если выражение внутри тега равно true, то элемент <small> будет показан на экране, если false - будет скрыт.

Выражение ${fields.hasErrors('lastName)} означает, есть ли ошибки валидации для поля lastName? Если ошибки есть - поле будет показано. Текст ошибки выводим с помощью атрибута th:errors.

Добавляем элементы для вывода ошибок для остальных полей формы. Проверяем результат

Ниже представлен листинг классов и файлов

hashtag
7. Загрузка файлов на сервер

index.html
<form enctype="multipart/form-data" action="#" th:action="@{/}" th:object="${student}" method="post" class="needs-validation">
    <div class="row">
        <div class="col-md-12">
            <h4 class="mb-3">Поля для заполнения</h4>
            <div class="row">
                <div class="col-md-6 mb-3">
                    <label for="lastName">Фамилия</label>
                    <input th:field="*{lastName}" type="text" class="form-control" id="lastName">
                </div>
                <div class="col-md-6 mb-3">
                    <label for="firstName">Имя</label>
                    <input th:field="*{firstName}" type="text" class="form-control" id="firstName">
                </div>
            </div>

            <div class="row">
                <div class="col-md-6 mb-3">
                    <label for="email">Электронная почта</label>
                    <input th:field="*{email}" type="text" class="form-control" id="email">
                </div>
            </div>

            <div class="row">
                <div class="col-md-6 mb-3">
                    <label for="age">Возраст</label>
                    <input th:field="*{age}" type="number" class="form-control" id="age">
                </div>
            </div>

            <hr class="mb-4">
            <button class="btn btn-primary btn-lg btn-block" type="submit" value="Submit">Добавить студента</button>
        </div>
    </div>
</form>
здесьarrow-up-right
pom.xml
<?xml version="1.0" encoding="UTF-8"?>
<project ...>

    ...

    <dependencies>
    
    ...

        <dependency>
            <groupId>javax.validation</groupId>
            <artifactId>validation-api</artifactId>
            <version>2.0.1.Final</version>
        </dependency>
    </dependencies>

...

</project>
Student.java
public class Student {

    // Имя должно быть длиной от 2 до 50 символов
    @Size(min = 2, max= 50, message = "First name should be from 2 to 50 characters")
    private String firstName;
    
    // Фамилия должна быть длиной от 2 до 50 символов
    @Size(min = 2, max= 50, message = "Last name should be from 2 to 50 characters")
    private String lastName;
    
    // Возраст должен быть целым числом от 13 до 65
    @Range(min = 13, max = 65, message = "Student age should be from 13 to 65 years")
    private int age;
    
    // Для валидации электронной почты используем регулярное выражение
    @Pattern(regexp = "^[\\\\w!#$%&’*+/=?`{|}~^-]+(?:\\\\.[\\\\w!#$%&’*+/=?`{|}~^-]+)*@(?:[a-zA-Z0-9-]+\\\\.)+[a-zA-Z]{2,6}$",
            message = "Invalid email format")
    private String email;
    
    ...
}
    @GetMapping("/")
    public String addStudent(Model model) {
        model.addAttribute("student", new Student());
        return "index";
    }

    @PostMapping("/")
    public String processAddStudentForm(@Valid Student student, BindingResult bindingResult) {

        if (bindingResult.hasErrors()) {
            System.out.println("Validation has been failed!");
            return "index";
        }

        System.out.println(student);

        list.add(student);
        return "redirect:/";
    }
index.html
<div class="col-md-6 mb-3">
    <label for="lastName">Фамилия</label>
    <input th:field="*{lastName}" type="text" class="form-control" id="lastName">
    <small class="text-danger" th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"/>
</div>
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <title>Добавление студента</title>
</head>
<body class="bg-light">
<div class="container">
    <div class="py-5 text-center">
        <h2>Добавление студента</h2>
        <p class="lead">Заполните поля и нажмите кнопку 'Добавить студента'</p>
    </div>

    <form enctype="multipart/form-data" action="#" th:action="@{/}" th:object="${student}" method="post">
        <div class="row">
            <div class="col-md-12">
                <h4 class="mb-3">Поля для заполнения</h4>
                <div class="row">
                    <div class="col-md-6 mb-3">
                        <label for="lastName">Фамилия</label>
                        <input th:field="*{lastName}" type="text" class="form-control" id="lastName">
                        <small class="text-danger" th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"/>
                    </div>
                    <div class="col-md-6 mb-3">
                        <label for="firstName">Имя</label>
                        <input th:field="*{firstName}" type="text" class="form-control" id="firstName">
                        <small class="text-danger" th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"/>
                    </div>
                </div>

                <div class="row">
                    <div class="col-md-6 mb-3">
                        <label for="email">Электронная почта</label>
                        <input th:field="*{email}" type="text" class="form-control" id="email">
                        <small class="text-danger" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"/>
                    </div>
                </div>

                <div class="row">
                    <div class="col-md-6 mb-3">
                        <label for="age">Возраст</label>
                        <input th:value="${student.age > 0} ? ${student.age} : ''" th:field="*{age}" type="number" class="form-control" id="age">
                        <small class="text-danger" th:if="${#fields.hasErrors('age')}" th:errors="*{age}"/>
                    </div>
                </div>

                <hr class="mb-4">
                <button class="btn btn-primary btn-lg btn-block" type="submit" value="Submit">Добавить студента</button>
            </div>
        </div>
    </form>
</div>
</div>
</div>

<br/><br/><br/>


<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
        integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
        crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
        integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
        crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
        integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
        crossorigin="anonymous"></script>
</body>
</html>
public class Student {

    // Имя должно быть длиной от 2 до 50 символов
    @Size(min = 2, max= 50, message = "First name should be from 2 to 50 characters")
    private String firstName;

    // Фамилия должна быть длиной от 2 до 50 символов
    @Size(min = 2, max= 50, message = "Last name should be from 2 to 50 characters")
    private String lastName;

    // Возраст должен быть целым числом от 13 до 65
    @Range(min = 13, max = 65, message = "Student age should be from 13 to 65 years")
    private int age;

    // Для валидации электронной почты используем регулярное выражение
    @Pattern(regexp = "^[a-zA-Z0-9_!#$%&’*+/=?`{|}~^.-]+@[a-zA-Z0-9.-]+$",
            message = "Invalid email format")
    private String email;

    public Student(String firstName, String lastName, int age, String email) {
        this.firstName = firstName;
        this.lastName = lastName;
        this.age = age;
        this.email = email;
    }

    public Student() {
    }

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }
}
@Controller
public class StudentController {

    private List<Student> list = new ArrayList<>();

    @GetMapping("/")
    public String addStudent(Model model) {
        model.addAttribute("student", new Student());
        return "index";
    }

    @PostMapping("/")
    public String processAddStudentForm(@Valid Student student, BindingResult bindingResult) {
        if (bindingResult.hasErrors()) {
            return "index";
        }

        System.out.println(student);

        list.add(student);
        return "redirect:/";
    }
}
<!doctype html>
<html lang="en" xmlns:th="http://www.thymeleaf.org">
<head>
    <!-- Required meta tags -->
    <meta charset="utf-8">
    <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no">
    <!-- Bootstrap CSS -->
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/css/bootstrap.min.css"
          integrity="sha384-Gn5384xqQ1aoWXA+058RXPxPg6fy4IWvTNh0E263XmFcJlSAwiGgFAW/dAiS6JXm" crossorigin="anonymous">
    <title>Добавление студента</title>
</head>
<body class="bg-light">
<div class="container">
    <div class="py-5 text-center">
        <h2>Добавление студента</h2>
        <p class="lead">Заполните поля и нажмите кнопку 'Добавить студента'</p>
    </div>

    <form enctype="multipart/form-data" action="#" th:action="@{/}" th:object="${student}" method="post">
        <div class="row">
            <div class="col-md-12">
                <h4 class="mb-3">Поля для заполнения</h4>
                <div class="row">
                    <div class="col-md-6 mb-3">
                        <label for="lastName">Фамилия</label>
                        <input th:field="*{lastName}" type="text" class="form-control" id="lastName">
                        <small class="text-danger" th:if="${#fields.hasErrors('lastName')}" th:errors="*{lastName}"/>
                    </div>
                    <div class="col-md-6 mb-3">
                        <label for="firstName">Имя</label>
                        <input th:field="*{firstName}" type="text" class="form-control" id="firstName">
                        <small class="text-danger" th:if="${#fields.hasErrors('firstName')}" th:errors="*{firstName}"/>
                    </div>
                </div>

                <div class="row">
                    <div class="col-md-6 mb-3">
                        <label for="email">Электронная почта</label>
                        <input th:field="*{email}" type="text" class="form-control" id="email">
                        <small class="text-danger" th:if="${#fields.hasErrors('email')}" th:errors="*{email}"/>
                    </div>
                </div>

                <div class="row">
                    <div class="col-md-6 mb-3">
                        <label for="age">Возраст</label>
                        <input th:value="${student.age > 0} ? ${student.age} : ''" th:field="*{age}" type="number" class="form-control" id="age">
                        <small class="text-danger" th:if="${#fields.hasErrors('age')}" th:errors="*{age}"/>
                    </div>
                </div>

                <div class="form-group">
                    <label for="file">Choose file:</label>
                    <input type="file" name="file" class="form-control-file" id="file">
                </div>

                <hr class="mb-4">
                <button class="btn btn-primary btn-lg btn-block" type="submit" value="Submit">Добавить студента</button>
            </div>
        </div>
    </form>
</div>
</div>
</div>

<br/><br/><br/>

<!-- Optional JavaScript -->
<!-- jQuery first, then Popper.js, then Bootstrap JS -->
<script src="https://code.jquery.com/jquery-3.2.1.slim.min.js"
        integrity="sha384-KJ3o2DKtIkvYIK3UENzmM7KCkRr/rE9/Qpg6aAZGJwFDMVNA/GpGFF93hXpG5KkN"
        crossorigin="anonymous"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"
        integrity="sha384-ApNbgh9B+Y1QKtv3Rn7W3mgPxhU9K/ScQsAP7hUibX39j7fakFPskvXusvfa0b4Q"
        crossorigin="anonymous"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0/js/bootstrap.min.js"
        integrity="sha384-JZR6Spejh4U02d8jOt6vLEHfe/JQGiRRSQQxSfFWpi1MquVdAyjUar5+76PVCmYl"
        crossorigin="anonymous"></script>
</body>
</html>