Лекция 9
Тема: Получение данных формы. Валидация данных, Bean Validation API. Загрузка файлов на сервер.
6. Валидация данных формы
Создадим проект с индексной страницей, на которой расположена форма добавления нового студента.

<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>Изначально, форма не имеет средств валидации, то есть мы не можем отследить корректность заполнения формы.
Spring предоставляет несколько инструментов для реализации валидации формы, воспользуемся библиотекой Bean Validation API
Добавим библиотеку в список зависимостей в файле 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. Добавим необходимые ограничения для полей сущности.
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;
...
}Как мы видим, все достаточно просто и наглядно.
Далее, нам необходимо модифицировать контроллеры и реализовать следующий функционал:
указать, что объект типа Student должен пройти валидацию;
получить результаты валидации объекта;
если объект не прошел валидацию - не добавлять объект в хранилище, выдать сообщение об ошибке в консоль.
Нам необходимо модифицировать метод контроллера, который обрабатывает данные формы. Указываем аннотацию @Valid, которая говорит о том, что полученный объект необходимо подвергнуть валидации. Далее указываем аргумент типа BindingResult, который хранит информацию о результате валидации. С помощью метода hasErrors() получаем результат валидации объекта.
@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.
Рассмотрим поле "Фамилия". Сообщение об ошибке мы разместим снизу поля. Добавим соответствующий элемент <small> в 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>Используем тег th:if. Если выражение внутри тега равно true, то элемент <small> будет показан на экране, если false - будет скрыт.
Выражение ${fields.hasErrors('lastName)} означает, есть ли ошибки валидации для поля lastName? Если ошибки есть - поле будет показано. Текст ошибки выводим с помощью атрибута th:errors.
Добавляем элементы для вывода ошибок для остальных полей формы. Проверяем результат

Ниже представлен листинг классов и файлов
<!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:/";
}
}7. Загрузка файлов на сервер
<!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>Last updated
Was this helpful?