总览

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

在处理空值时,我们使用了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

Thymeleaf + Spring中的验证相关推荐

  1. spring+thymeleaf实现表单验证数据双向绑定

    前言 这个教程介绍了Thymeleaf与Spring框架的集成,特别是SpringMvc框架. 注意Thymeleaf支持同Spring框架的3.和4.版本的集成,但是这两个版本的支持是封装在thym ...

  2. Spring MVC中的验证组

    可以通过groups属性将Bean验证中的验证约束添加到一个或多个组. 这使您可以限制在验证期间应用的约束集. 在某些小组应先于其他小组进行验证(例如在向导中)的情况下,这可能很方便. 从Spring ...

  3. 一.spring中的各个依赖,及分类

    一.spring中的各个依赖,及分类 (1)spring基础包 (一)概览 (二)依赖代码 (2)AOP切面 (一)概览 (二)依赖代码 (3)事务 (一)概览 (二)相关代码 (4)数据库 (一)概 ...

  4. Thymeleaf+Spring整合

    前言 这个教程介绍了Thymeleaf与Spring框架的集成,特别是SpringMvc框架. 注意Thymeleaf支持同Spring框架的3.和4.版本的集成,但是这两个版本的支持是封装在thym ...

  5. Thymeleaf+Spring整合(官方文档翻译)

    官方英文文档地址 https://www.thymeleaf.org/doc/tutorials/2.1/thymeleafspring.html 前言 这个教程介绍了Thymeleaf与Spring ...

  6. Spring中配置DataSource数据源的几种选择

    Spring中配置DataSource数据源的几种选择 在Spring框架中有如下3种获得DataSource对象的方法: 从JNDI获得DataSource. 从第三方的连接池获得DataSourc ...

  7. 【Spring源码】Spring中的AOP底层原理分析

    AOP中的几个概念 Advisor 和 Advice Advice,我们通常都会把他翻译为通知,其实很不好理解,其实他还有另外一个意思,就是"建议",我觉得把Advice理解为&q ...

  8. 如何在优雅地Spring 中实现消息的发送和消费

    本文将对rocktmq-spring-boot的设计实现做一个简单的介绍,读者可以通过本文了解将RocketMQ Client端集成为spring-boot-starter框架的开发细节,然后通过一个 ...

  9. Spring中ref local与ref bean区别

    为什么80%的码农都做不了架构师?>>>    Spring中ref local与ref bean区别 今天在做SSH框架Demo实例时,在ApplicationResources. ...

最新文章

  1. iframe中请求页面而session失效时页面跳转问题
  2. WinDbg配置和使用基础
  3. 电脑上怎么做pdf文件_PDF文件怎么加水印?PDF水印添加方法
  4. 双字节数据 先低后高_1.4 C++数据类型
  5. T-SQL查询高级—SQL Server索引中的碎片和填充因子
  6. Socket支持多用户并发访问的解决办法
  7. WCF系列学习笔记4之绑定详解
  8. 类加载器-启动类加载器
  9. VHDL程序基本构建
  10. TeaPot 用webgl画茶壶(3) 环境纹理和skybox
  11. mercurial和svn_DBA Mercurial简介–分支和合并
  12. 阅读材料:信息技术年谱
  13. 开灯问题 算法竞赛 (注释详细)
  14. Win10问题篇之——WIN2016和WIN10关闭同步主机服务,节省磁盘频繁读取,并关闭自动维护
  15. 敏感性、特异性、假阳性、假阴性(sensitivity and specificity)
  16. 独家专访李林 | 回归火币后的一年
  17. 如何对网站关键词进行合理布局?
  18. OneNote for windows10卸载及重装
  19. 快解析教你,快速修改3389端口
  20. Angular-CLI工具使用文档翻译

热门文章

  1. Zookeeper选举原理——FastLeaderElection
  2. 一分钟理解Java公平锁与非公平锁
  3. artTemplate的空白输出坑
  4. 新闻发布项目——接口类(categoryTBDao)
  5. spark submit参数及调优
  6. 三星系统和鸿蒙系统,又一设备直升鸿蒙系统,现有操作系统被抛弃,和三星的想法一样!...
  7. java 路径 20,java中得到classpath和当前类的绝对路径的一些方法(路径中的%20进行替换空格)...
  8. 解决eclipse中tomcat无法识别maven web项目问题
  9. 滴滴2017在线笔试有感
  10. 索引---B+Tree