Thymeleaf + Spring中的验证

总览

我们将要讨论的重要主题涉及空值,空字符串和输入验证,因此我们不会在数据库中输入无效数据。

在处理空值时,我们使用了Java 1.8中引入的java.util.Optional

0 – Spring Boot + Thymeleaf示例表单验证应用程序

我们正在为一所大学构建一个Web应用程序,使潜在的学生可以请求有关其课程的信息。

查看并从 Github 下载代码

1 –项目结构

2 –项目依赖性

除了典型的Spring Boot依赖关系之外,我们还在 LEGACYHTML5模式下使用嵌入式HSQLDB数据库和nekohtml 。

<?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"xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><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 –模型

在我们的模型中,我们定义:

  • 自动生成的ID字段
  • 名称字段不能为空
  • 名称必须在2到40个字符之间
  • @Email批注验证的电子邮件字段
  • 布尔字段“开放日”,允许潜在学生指出她是否想参加开放日
  • 布尔字段“订阅”,用于订阅电子邮件更新
  • 注释字段是可选的,因此没有最低字符要求,但有最高字符要求
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;@Entity
public class Student {@Id@GeneratedValue(strategy = GenerationType.AUTO)private Long id;@NotNull@Size(min=2, max=40)private String name;@NotNull@Emailprivate 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 –储存库

我们定义一个存储库。

package com.michaelcgood.dao;import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;import com.michaelcgood.model.Student;@Repository
public interface StudentRepository extends JpaRepository<Student,Long> {}

5 –控制器

我们注册StringTrimmerEditor将空字符串自动转换为空值。

当用户发送POST请求时,我们希望接收该Student对象的值,因此我们使用@ModelAttribute来做到这一点。

为确保用户发送的值有效,我们接下来使用适当命名的@Valid批注。

BindingResult必须紧随其后,否则在提交无效数据而不是保留在表单页面时, 将为用户提供错误页面。

我们使用if ... else来控制用户提交表单时发生的情况。 如果用户提交了无效数据,则该用户将保留在当前页面上,而在服务器端将不再发生任何事情。 否则,应用程序将使用用户的数据,并且用户可以继续。

在这一点上,检查学生的姓名是否为空是多余的,但是我们这样做。 然后,我们调用下面定义的方法checkNullString ,以查看注释字段是空的String还是null。

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;@Controller
public class StudentController {@InitBinderpublic void initBinder(WebDataBinder binder) {binder.registerCustomEditor(String.class, new StringTrimmerEditor(true));}public String finalString = null;@Autowiredprivate 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(str); 表示字符串将成为数据类型Optional,但是字符串可以为空值。

endString = opt.orElse(“ None”); 如果变量opt为null,则将String值设置为“ None”。

6 – Thymeleaf模板

正如您在上面的Controller映射中所看到的,有两个页面。 index.html是我们的主页,具有针对潜在大学生的表格。

我们的主要对象是学生,因此我们的th:object当然是指该对象 。 我们模型的字段分别进入th:field

我们将表单的输入包装在表格中以进行格式化。

在每个表单元格(td)下,我们都有一个条件语句,如下所示: […] th:if =” $ {#fields.hasErrors('name')}“ th:errors =” * {name}”
[…]

上面的条件语句意味着,如果用户在该字段中输入的数据与我们在Student模型中对该字段的要求不符,然后提交表单,则在用户返回此页面时显示输入要求。

index.html

<html xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org"><head>
<!-- CSS INCLUDE -->
<link rel="stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"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 UniversityInfo</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}">NameError</td></tr><tr><td>Email:</td><td><input type="text" th:field="*{email}"></input></td><td th:if="${#fields.hasErrors('email')}" th:errors="*{email}">EmailError</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><script src="https://code.jquery.com/jquery-1.11.1.min.js"integrity="sha256-VAvG3sHdS5LqTT+5A/aeq/bZGa/Uj04xKxY8KM/w9EE="crossorigin="anonymous"></script><scriptsrc="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"crossorigin="anonymous"></script></body>
</html>

这是用户成功完成表单后看到的页面。 我们使用th:text向用户显示他或她在该字段中输入的文本。

Thanks.html

<html xmlns="http://www.w3.org/1999/xhtml"xmlns:th="http://www.thymeleaf.org"><head>
<!-- CSS INCLUDE -->
<link rel="stylesheet"href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css"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><scriptsrc="https://code.jquery.com/jquery-1.11.1.min.js"integrity="sha256-VAvG3sHdS5LqTT+5A/aeq/bZGa/Uj04xKxY8KM/w9EE="crossorigin="anonymous"></script><scriptsrc="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"integrity="sha384-Tc5IQib027qvyjSMfHjOMaLkfuWVxZxUPnCJA7l2mCWNIpG9mGCD8wGNIcPD7Txa"crossorigin="anonymous"></script></body>
</html>

7 –配置

使用Spring Boot Starter并包括Thymeleaf依赖项,您将自动拥有/ templates /的模板位置,并且Thymeleaf可以直接使用。 因此,不需要大多数这些设置。

要注意的一个设置是nekohtml提供的LEGACYHTM5 。 如果需要,这使我们可以使用更多随意HTML5标签。 否则,Thymeleaf将非常严格,并且可能无法解析您HTML。 例如,如果您不关闭输入标签,Thymeleaf将不会解析您HTML。

application.properties

#==================================
# = Thymeleaf configurations 
#==================================
spring.thymeleaf.check-template-location=true
spring.thymeleaf.prefix=classpath:/templates/
spring.thymeleaf.suffix=.html
spring.thymeleaf.content-type=text/html
spring.thymeleaf.cache=false
spring.thymeleaf.mode=LEGACYHTML5server.contextPath=/

8 –演示

主页

在这里,我们到达主页。

无效数据

我在名称字段和电子邮件字段中输入了无效数据。

有效数据,无评论

现在,我在所有字段中输入了有效数据,但未提供注释。 不需要提供评论。 在我们的控制器中,我们使所有空字符串都为空值。 如果用户未提供注释,则将String值设置为“ None”。

9 –结论

包起来

该演示应用程序演示了如何以Thymeleaf形式验证用户输入。
我认为,Spring和Thymeleaf与javax.validation.constraints一起可以很好地验证用户输入。
源代码在 Github上

笔记

Java 8的Optional出于某种演示目的而被强加到该应用程序中,我想指出的是,当使用@RequestParam时 ,如我的PagingAndSortingRepository教程中所示,它可以更有机地工作。

但是,如果您不使用Thymeleaf,则可以将我们不需要的字段设置为Optional 。 Vlad Mihalcea在这里讨论了使用JPA和Hibernate映射Optional实体属性的最佳方法 。

翻译自: https://www.javacodegeeks.com/2017/10/validation-thymeleaf-spring.html

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/349621.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

技术管理规划-设定团队的职能

背景 职责 团队是干什么的 初步自查团队 1.公司为什么给我团队&#xff1f;希望我产出什么&#xff1f;完成对除了c端健康领域探索的研发任务&#xff0c;产出技术类产品 2.团队存在的独特价值是什么&#xff1f;研发过lx健康这款基础app,研发能力强&#xff0c;熟悉硬件相关技…

开发转测试没人要_前端开发,测试,后端,该如何选择?

一般来说前端会比后端简单一些的&#xff0c;初学者或者转行可能考虑前端多一点&#xff0c;但是后端开发的薪水又比前端高一些&#xff0c;就是比较枯燥。前端开发我目前一直在自学前端&#xff0c;从网上找资料&#xff0c;然后听课&#xff0c;只要是对编程有兴趣&#xff0…

技术管理规划-如何设定团队的目标

团队管理规划有4个互相关联的要素&#xff1a; 职能目标团队路径在未来的3个月&#xff0c;6个月&#xff0c;1年&#xff0c;2年&#xff0c;3年&#xff0c;5年中&#xff0c;你希望带着你的团队抵达一个什么样的目的地&#xff0c;也就是团队的目标。 更加清楚目标意味着什么…

a8处理器相当于骁龙几_天玑820相当于骁龙什么处理器?天梯图秒懂联发科天玑820性能排名...

5月18日&#xff0c;联发科发布了全新 天玑820处理器&#xff0c;号称目前最强中端芯&#xff0c;受到不少网友关注。联发科近年来在手机CPU市场沉寂多年&#xff0c;今年异常给力&#xff0c;先后发布了天玑1000/L、天玑800等多款处理器&#xff0c;采用了全新的命名方式&…

hibernate 继承_Hibernate继承:每个类层次结构的表

hibernate 继承在本教程中&#xff0c;我们将了解如何在hibernate中实现继承。可以通过3种方式在hibernate中实现继承。在本文中&#xff0c;我们将看到其中一种&#xff0c;即每个类层次结构一个表。 Hibernate中的继承&#xff1a; Java是面向对象的语言&#xff0c;继承是J…

vue - cli 脚手架安装

一、 node安装 1&#xff09;如果不确定自己是否安装了node,可以在命令行工具内执行&#xff1a; node -v &#xff08;检查一下 版本&#xff09;&#xff1b; 2&#xff09;如果 执行结果显示&#xff1a; xx 不是内部命令&#xff0c;说明你还没有安装node , node 安装地址…

python人工智能_人工智能福利丨Python核心语法实战

Python已正式跻身成熟语言行列&#xff0c;成为整个互联网的基础性语言之一&#xff0c;并以肉眼可见的速度&#xff0c;在全球攻城略地&#xff1a;——牢牢占据TIOBE世界编程语言排行榜第四名&#xff0c;且保持上升趋势——国家级人工智能四大平台确立——正式纳入全国计算机…

.NET Core 单元测试

应用程序测试的类型很多&#xff0c;包括集成测试&#xff0c;Web 测试&#xff0c;负载测试等。在最底层的是单元测试&#xff0c;此测试可以测试单个软件组件或方法。单元测试一般只测试开发人员的代码&#xff0c;不应该测试基础结构普、问题&#xff0c;如数据库&#xff0…

是什么原因导致OutOfMemoryError?

发生以下情况之一时&#xff0c;可能会引发OutOfMemoryError &#xff1a; JVM耗尽了本机内存 Java堆内存不足 PermGen或Metaspace内存不足 JVM花太多时间试图收集垃圾 通常可以从错误消息中OutOfMemoryError出OutOfMemoryError的根本原因。 让我们研究每种情况的细节。 …

win10电脑开机密码忘了怎么办_Mac电脑忘记开机密码怎么办?Mac开机密码快速恢复方法...

Mac忘记开机密码怎么办&#xff1f;虽然小编觉得大多数人应该都不会Mac忘记开机密码&#xff0c;但是如果真的有人忘记了怎么办呢&#xff1f;小编这里教你们一种方法&#xff0c;可以帮你1分钟快速恢复Mac电脑忘记开机密码&#xff0c;感兴趣的朋友快跟着小编一起来看看吧&…

技术管理规划-如何规划团队的架构

管理规划的4个要素 1.职能【清楚自己团队的基本职责和使命】 2.目标【为团队设定清晰的目标】 3.团队【团队的架构规划】 4.路径 团队目标 根据团队目标去梳理团队 团队目标&#xff1a; 某个时间节点&#xff0c;团队发展成什么状态。 要点说明规模实际人数和预算人数分工团队…

win10家庭版调出组策略_利用powershell为win10家庭版安装组策略

虽然win10家庭版阉割了组策略管理器&#xff0c;但至少到1607版本时还可以用cmd脚本安装它。可是随后win10更新了几个大版本&#xff0c;不知为何&#xff0c;cmd控制台常常会有诡异的Bug&#xff0c;导致无法用老办法安装组策略管理器。为了彻底解决这个问题&#xff0c;不妨另…

Spring批处理CSV处理

总览 我们将讨论的主题包括使用Spring Batch进行批处理的基本概念&#xff0c;以及如何将数据从CSV导入数据库。 0 – Spring Batch CSV处理示例应用程序 我们正在构建一个应用程序&#xff0c;该应用程序演示Spring Batch处理CSV文件的基础。 我们的演示应用程序将允许我们处…

NOIP模拟测试28「阴阳·虎·山洞」

写这几个题解我觉得我就像在按照官方题解抄一样 阴阳 题解 将题目中给的阴阳看作黑色和白色 首先我们观察到最后生成图中某种颜色必须是竖着单调递增或竖着单调递减 类似这样 否则不满足这个条件 但合法染色方案必须满足任意两个同颜色格子之间的格子也必须是该颜色。 然后我们…

linux设置环境变量_什么是linux环境变量

本来这篇文章好几天之前就写好了&#xff0c;但是媳妇儿跟我说工作日就不要发了&#xff0c;大家都在上班&#xff0c;哪有闲心思看你的文章。哎&#xff0c;可能大家用头条都是在放松刷娱乐&#xff0c;看小姐姐。所以就一直拖到现在。周末了&#xff0c;更是放松的好时候&…

理科卷math·english·chinese·biology·chemistry·physics

一套比一套炸,果然我只会做B卷,虽然我B也很差但没差到这种地步 $math$ 题解 看似没法做但总会有突破口 $70\%$ 发现和小凯的诱惑很像,于是看$gcd$是否为$1$只要为$1$可以凑齐所有数 $n^2$枚举两两$gcd$ $80\%$ 我考试时思路 找到每一个数和$mod$的$gcd$,发现只要是任一$gcd$倍数…

cad卸载_怎么把CAD卸载干净,老司机来教你

CAD经常出现文件丢失啊、这样那样的提示&#xff0c;要是身边有个大神级朋友还好&#xff0c;没有的小盆友只能乖乖的卸载&#xff0c;重新安装了&#xff0c;那么又有个问题拦住我们了——怎么把CAD卸载干净呢&#xff1f;由于卸载不干净&#xff0c;再次安装CAD时&#xff0c…

什么是JavaServer Faces(JSF)–(第2部分)

Facelets声明语言 在第1部分中&#xff0c;我介绍了JavaServer Pages&#xff08;JSF&#xff09;背后的基本思想 。 在本文中&#xff0c;我想介绍Facelets声明语言 。 HTML标签 我们遇到的第一个标签是代表HTML元素HTML标签。 这些实际上只是HTML标记&#xff08;例如输入&a…

问题 1076: 内部收益率

问题 1076: 内部收益率 时间限制: 1Sec 内存限制: 128MB 提交: 418 解决: 169 题目描述在金融中&#xff0c;我们有时会用内部收益率IRR来评价项目的投资财务效益&#xff0c;它等于使得投资净现值NPV等于0的贴现率。换句话说&#xff0c;给定项目的期数T、初始现金流CF0和项目…

路由器上的usb接口有什么用_路由器的USB接口,非常强大的功能,教您轻轻松松玩转,太实用了...

新一代出来的路由器后面基本上都会有一个或者是两个以上的有USB接口。居然还有很多人都不知道这些&#xff0c;要是比接口的用处。只是把它当做普通的无线路由器用。这样子太可惜了。其实路由器后面的usb接口呀&#xff0c;它有非常强大的功能&#xff0c;好处多多。接下来就请…