обзор
Важные темы, которые мы будем обсуждать, касаются нулевых значений, пустых строк и проверки ввода, поэтому мы не вводим неверные данные в нашу базу данных.
При работе с нулевыми значениями мы коснемся использования java.util.Optional, который был представлен в Java 1.8.
0 — Пример проверки формы Spring Boot + Thymeleaf
Мы создаем веб-приложение для университета, которое позволяет потенциальным студентам запрашивать информацию о своих программах.
Посмотреть и скачать код с Github
1 — Структура проекта
2 — Зависимости проекта
Помимо наших типичных зависимостей Spring Boot, мы используем встроенную базу данных HSQLDB и nekohtml для режима LEGACYHTML5.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
|
<?xml version="1.0" encoding="UTF-8"?><project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" <modelVersion>4.0.0</modelVersion> <groupId>com.michaelcgood</groupId> <artifactId>michaelcgood-validation-thymeleaf</artifactId> <version>0.0.1</version> <packaging>jar</packaging> <name>michaelcgood-validation-thymeleaf</name> <description>Michael C Good - Validation in Thymeleaf Example Application</description> <parent> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-parent</artifactId> <version>1.5.7.RELEASE</version> <relativePath/> <!-- lookup parent from repository --> </parent> <properties> <project.build.sourceEncoding>UTF-8</project.build.sourceEncoding> <project.reporting.outputEncoding>UTF-8</project.reporting.outputEncoding> <java.version>1.8</java.version> </properties> <dependencies> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-jpa</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-thymeleaf</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency> <dependency> <groupId>org.hsqldb</groupId> <artifactId>hsqldb</artifactId> <scope>runtime</scope> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-test</artifactId> <scope>test</scope> </dependency> <!-- legacy html allow --> <dependency> <groupId>net.sourceforge.nekohtml</groupId> <artifactId>nekohtml</artifactId> <version>1.9.21</version> </dependency> </dependencies> <build> <plugins> <plugin> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-maven-plugin</artifactId> </plugin> </plugins> </build></project> |
3 — Модель
В нашей модели мы определяем:
- Сгенерированное автоматически поле идентификатора
- Поле имени, которое не может быть пустым
- Что имя должно быть от 2 до 40 символов
- Поле электронной почты, которое проверяется аннотацией @Email
- Булево поле «openhouse», которое позволяет потенциальному студенту указать, хочет ли он посещать день открытых дверей
- Логическое поле «подписаться» для подписки на обновления по электронной почте
- Поле комментариев, которое является необязательным, поэтому не требуется минимальное требование к символу, но есть максимальное требование к символу
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
|
package com.michaelcgood.model;import javax.persistence.Entity;import javax.persistence.GeneratedValue;import javax.persistence.GenerationType;import javax.persistence.Id;import javax.validation.constraints.NotNull;import javax.validation.constraints.Size;import org.hibernate.validator.constraints.Email;@Entitypublic class Student { @Id @GeneratedValue(strategy = GenerationType.AUTO) private Long id; @NotNull @Size(min=2, max=40) private String name; @NotNull @Email private String email; private Boolean openhouse; private Boolean subscribe; @Size(min=0, max=300) private String comments; public Long getId() { return id; } public void setId(Long id) { this.id = id; } 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 Boolean getOpenhouse() { return openhouse; } public void setOpenhouse(Boolean openhouse) { this.openhouse = openhouse; } public Boolean getSubscribe() { return subscribe; } public void setSubscribe(Boolean subscribe) { this.subscribe = subscribe; } public String getComments() { return comments; } public void setComments(String comments) { this.comments = comments; } } |
4 — Репозиторий
Мы определяем хранилище.
|
01
02
03
04
05
06
07
08
09
10
11
|
package com.michaelcgood.dao;import org.springframework.data.jpa.repository.JpaRepository;import org.springframework.stereotype.Repository;import com.michaelcgood.model.Student;@Repositorypublic interface StudentRepository extends JpaRepository<Student,Long> {} |
5 — Контроллер
Мы регистрируем StringTrimmerEditor для автоматического преобразования пустых строк в нулевые значения.
Когда пользователь отправляет запрос POST, мы хотим получить значение этого объекта Student, поэтому для этого мы используем @ModelAttribute .
Чтобы убедиться, что пользователь отправляет допустимые значения, мы используем соответствующую аннотацию @Valid .
Далее следует BindingResult , в противном случае пользователю выдается страница с ошибкой при отправке неверных данных вместо того, чтобы оставаться на странице формы.
Мы используем if … else для контроля того, что происходит, когда пользователь отправляет форму. Если пользователь отправляет неверные данные, он останется на текущей странице, и больше ничего не произойдет на стороне сервера. В противном случае приложение будет использовать данные пользователя, и пользователь сможет продолжить.
На данный момент, это несколько избыточно, чтобы проверить, является ли имя студента нулевым, но мы делаем. Затем мы вызываем метод checkNullString , который определен ниже, чтобы увидеть, является ли поле комментария пустой строкой или пустым.
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
|
package com.michaelcgood.controller;import java.util.Optional;import javax.validation.Valid;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.beans.propertyeditors.StringTrimmerEditor;import org.springframework.stereotype.Controller;import org.springframework.ui.Model;import org.springframework.validation.BindingResult;import org.springframework.web.bind.WebDataBinder;import org.springframework.web.bind.annotation.GetMapping;import org.springframework.web.bind.annotation.InitBinder;import org.springframework.web.bind.annotation.ModelAttribute;import org.springframework.web.bind.annotation.PostMapping;import com.michaelcgood.dao.StudentRepository;import com.michaelcgood.model.Student;@Controllerpublic class StudentController { @InitBinder public void initBinder(WebDataBinder binder) { binder.registerCustomEditor(String.class, new StringTrimmerEditor(true)); } public String finalString = null; @Autowired private StudentRepository studentRepository; @PostMapping(value="/") public String addAStudent(@ModelAttribute @Valid Student newStudent, BindingResult bindingResult, Model model){ if (bindingResult.hasErrors()) { System.out.println("BINDING RESULT ERROR"); return "index"; } else { model.addAttribute("student", newStudent); if (newStudent.getName() != null) { try { // check for comments and if not present set to 'none' String comments = checkNullString(newStudent.getComments()); if (comments != "None") { System.out.println("nothing changes"); } else { newStudent.setComments(comments); } } catch (Exception e) { System.out.println(e); } studentRepository.save(newStudent); System.out.println("new student added: " + newStudent); } return "thanks"; } } @GetMapping(value="thanks") public String thankYou(@ModelAttribute Student newStudent, Model model){ model.addAttribute("student",newStudent); return "thanks"; } @GetMapping(value="/") public String viewTheForm(Model model){ Student newStudent = new Student(); model.addAttribute("student",newStudent); return "index"; } public String checkNullString(String str){ String endString = null; if(str == null || str.isEmpty()){ System.out.println("yes it is empty"); str = null; Optional<String> opt = Optional.ofNullable(str); endString = opt.orElse("None"); System.out.println("endString : " + endString); } else{ ; //do nothing } return endString; }} |
Optional.ofNullable (ул); означает, что String станет типом данных Optional, но String может иметь нулевое значение.
endString = opt.orElse («Нет»); устанавливает значение String равным «None», если переменная opt равна нулю.
6 — Шаблоны из тимьяна
Как вы видели в отображении нашего контроллера выше, есть две страницы. Index.html — это наша главная страница, которая имеет форму для потенциальных студентов университета.
Нашим основным объектом является Student, поэтому, конечно, наш объект th: относится к этому. Поля нашей модели соответственно идут в th: field .
Мы обертываем входные данные нашей формы в таблицу для форматирования.
Ниже каждой ячейки таблицы (td) у нас есть условный оператор, подобный этому: […] th: if = ”$ {# fields.hasErrors (‘name’)}” th: errors = ”* {name}»
[…]
Вышеприведенный условный оператор означает, что если пользователь вводит данные в это поле, которые не соответствуют требованию, которое мы вводим для этого поля в нашей модели Стьюдента, а затем отправляет форму, показывать требования к вводу, когда пользователь возвращается на эту страницу.
index.html
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
|
<head><!-- CSS INCLUDE --><link rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"></link><!-- EOF CSS INCLUDE --></head><body> <!-- START PAGE CONTAINER --> <div class="container-fluid"> <!-- PAGE TITLE --> <div class="page-title"> <h2> <span class="fa fa-arrow-circle-o-left"></span> Request University Info </h2> </div> <!-- END PAGE TITLE --> <div class="column"> <form action="#" th:action="@{/}" th:object="${student}" method="post"> <table> <tr> <td>Name:</td> <td><input type="text" th:field="*{name}"></input></td> <td th:if="${#fields.hasErrors('name')}" th:errors="*{name}">Name Error</td> </tr> <tr> <td>Email:</td> <td><input type="text" th:field="*{email}"></input></td> <td th:if="${#fields.hasErrors('email')}" th:errors="*{email}">Email Error</td> </tr> <tr> <td>Comments:</td> <td><input type="text" th:field="*{comments}"></input></td> </tr> <tr> <td>Open House:</td> <td><input type="checkbox" th:field="*{openhouse}"></input></td> </tr> <tr> <td>Subscribe to updates:</td> <td><input type="checkbox" th:field="*{subscribe}"></input></td> </tr> <tr> <td> <button type="submit" class="btn btn-primary">Submit</button> </td> </tr> </table> </form> </div> <!-- END PAGE CONTENT --> <!-- END PAGE CONTAINER --> </div> integrity="sha256-VAvG3sHdS5LqTT+5A/aeq/bZGa/Uj04xKxY8KM/w9EE=" crossorigin="anonymous"></script> <script integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script></body></html> |
Здесь у нас есть страница, которую видит пользователь, когда он успешно заполнил форму. Мы используем th: text, чтобы показать пользователю текст, который он или она вводит для этого поля.
thanks.html
|
01
02
03
04
05
06
07
08
09
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
|
<head><!-- CSS INCLUDE --><link rel="stylesheet" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"></link><!-- EOF CSS INCLUDE --></head><body> <!-- START PAGE CONTAINER --> <div class="container-fluid"> <!-- PAGE TITLE --> <div class="page-title"> <h2> <span class="fa fa-arrow-circle-o-left"></span> Thank you </h2> </div> <!-- END PAGE TITLE --> <div class="column"> <table class="table datatable"> <thead> <tr> <th>Name</th> <th>Email</th> <th>Open House</th> <th>Subscribe</th> <th>Comments</th> </tr> </thead> <tbody> <tr th:each="student : ${student}"> <td th:text="${student.name}">Text ...</td> <td th:text="${student.email}">Text ...</td> <td th:text="${student.openhouse}">Text ...</td> <td th:text="${student.subscribe}">Text ...</td> <td th:text="${student.comments}">Text ...</td> </tr> </tbody> </table> </div> </div> <!-- END PAGE CONTAINER --> </div> <script integrity="sha256-VAvG3sHdS5LqTT+5A/aeq/bZGa/Uj04xKxY8KM/w9EE=" crossorigin="anonymous"></script> <script integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa" crossorigin="anonymous"></script></body></html> |
7 — Конфигурация
Используя Spring Boot Starter и включая зависимости Thymeleaf, у вас автоматически будет расположение шаблонов / templates / , а Thymeleaf просто работает из коробки. Так что большинство этих настроек не нужны.
Один параметр, на который следует обратить внимание — это LEGACYHTM5, предоставленный nekohtml . Это позволяет нам использовать более случайные теги HTML5, если мы хотим. В противном случае Thymeleaf будет очень строгим и может не проанализировать ваш HTML. Например, если вы не закроете входной тег, Thymeleaf не будет анализировать ваш HTML.
application.properties
|
01
02
03
04
05
06
07
08
09
10
11
|
#==================================# = Thymeleaf configurations #==================================spring.thymeleaf.check-template-location=truespring.thymeleaf.prefix=classpath:/templates/spring.thymeleaf.suffix=.htmlspring.thymeleaf.content-type=text/htmlspring.thymeleaf.cache=falsespring.thymeleaf.mode=LEGACYHTML5server.contextPath=/ |
8 — Демо
Главная страница
Здесь мы приходим на главную страницу.

Неверные данные
Я ввожу неверные данные в поле имени и адрес электронной почты.
Допустимые данные без комментариев
Теперь я размещаю достоверные данные во всех полях, но не предоставляю комментарий. Не требуется предоставлять комментарий. В нашем контроллере мы сделали все пустые строки пустыми значениями. Если пользователь не предоставил комментарий, значение String устанавливается как «None».
9 — Заключение
Заворачивать
Это демонстрационное приложение продемонстрировало, как проверять пользовательский ввод в форме Thymeleaf.
На мой взгляд, Spring и Thymeleaf хорошо работают с javax.validation.constraints для проверки ввода пользователя.
Исходный код есть на Github
Заметки
Необязательный Java 8 был своего рода принудительным в этом приложении для демонстрационных целей, и я хочу отметить, что он работает более органично при использовании @RequestParam, как показано в моем руководстве PagingAndSortingRepository .
Однако, если вы не используете Thymeleaf, вы могли бы сделать наши необязательные поля необязательными . Здесь Влад Михалча обсуждает лучший способ сопоставить атрибут необязательной сущности с JPA и Hibernate .
| Опубликовано на Java Code Geeks с разрешения Майкла Гуда, партнера нашей программы JCG . Смотреть оригинальную статью здесь: Проверка в Thymeleaf + Spring
Мнения, высказанные участниками Java Code Geeks, являются их собственными. |


