SpringMVC4+thymeleaf3的一个简单实例(篇四:form表单数据验证)
关于表单数据验证有很多中方法,这里我仅介绍JSR303注解验证。
JSR303仅仅是一个规范,这里我们要用到它的一个实现:hibernate-validator。
注意在spring的配置文件spring-mvc.xml中要有这句代码:<mvc:annotation-driven/>,有了它,spring框架会自动加载classpath的jsr303的实现。
开工之前,我们需要引入以下lib文件到WEB-INF/lib,并添加到classpath:
validation-api-1.1.0.Final.jar
classmate-1.3.1.jar
jboss-logging-3.3.0.Final.jar
hibernate-validator-5.3.2.Final.jar
以上jar文件都在hibernate-validator-5.3.2.Final-dist.zip这里,官网下载http://hibernate.org/validator/
延续前面篇节的内容。
一、修改AnimalForm.Java类,在oname,ocount,memo字段上分别加上验证注解,代码如下:
package com.zoo.web.form; import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size; import org.hibernate.validator.constraints.NotEmpty;
import org.hibernate.validator.constraints.Range; public class AnimalForm { private long id; @NotEmpty(message="动物名: 不能为空")
private String oname; @Range(min = 1, message="数量: 必须大于0")
@NotNull(message="数量: 不能为空")
private int ocount; @Size(max = 10, message="备注: 长度不能超过10个字符")
private String memo; /** 省略getter和setter **/ }
解释:
@NotEmpty:这个注解表示检查oname字段是不是为空字符串""或者是不是为null,如果是则给出提示信息:"动物名:不能为空"。
它支持的类型包括:字符序列CharSequence(CharBuffer, Segment, String, StringBuffer, StringBuilder);集合Collection(ArrayList, HashSet, Stack, Vector等,很多);Map以及数组arrays。它将检查所给对象的是不是为empty或者null,empty也就是长度为0,对于字符串来说就是""。
@NotNull:检查所标注元素ocount不能为null,如果是则给出提示信息:“数量:不能为空”。
它支持任意类型,检查标注对象是否为null。注意和@NotEmpty的区别,她不检查对象是不是为empty。empty对于字符串来说是空字符串,对于集合以及map或数组来说就是所含元素数量为0。
@Range(min=, max=):表示ocount元素的最小值是1,如果小于1,则给出信息:“数量:必须大于0”。
支持类型:BigDecimal, BigInteger, CharSequence, byte, short, int, long 以及这些原始类型对应的wrapper(包装类)。它将检查所给对象的值是不是大于等于min且小于等于max。
@Size(min=, max=):检查memo对象的长度不能超过10, 否则提示:“备注:长度不能超过10个字符”。
适用于CharSequence, Collection, Map 以及数组,检查标注对象的size是大于等于min并且小于等于max。
注意这么做验证是有问题的,比如oname输入几个空格它会验证通过,而对于ocount,在输入整数的情况下这完全没有问题,但是如果我们输入带小数点的数字或者输入非数字,或者空字符串的时候程序就会出现exception,这不是我们所希望的,具体的改进代码我们在篇末说明。
关于hibernate validator详细介绍请参阅 reference http://docs.jboss.org/hibernate/stable/validator/reference/en-US/html_single/
二、修改ZooController里的doAdd方法:
@RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)
public String doAdd(Model model, @Valid AnimalForm form, BindingResult result){
System.out.println("动物名:" + form.getOname());
System.out.println("数量:" + form.getOcount());
System.out.println("备注:" + form.getMemo());
if(result.hasErrors()){
model.addAttribute("MSG", "出错啦!");
}else{
model.addAttribute("MSG", "提交成功!");
}
return "zoolist";
}
解释:
方法中Model参数,用于存放任意数据以便传递到页面,注意Model仅仅是一个接口,spring框架会帮我们实例化具体的类并设置到该方法当中;上例我们在该model里放了一个key为“MSG”的attribute,页面上通过表达式就可以取得其值。
@Valid AnimalForm form,@Valid表示要对该form进行验证,具体验证规则就是根据上面【一】里提到;spring框架会根据字段名称将页面传递过来的值绑定到animalForm中。
BindingResult result,spring框架会将验证结果设置到该参数,并将该参数放到model传递给页面。
springMVC是非常灵活的,以下几种写法可以达到同样的效果:
(1)
@RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)
public ModelAndView doAdd(@Valid AnimalForm form, BindingResult result){
ModelAndView model = new ModelAndView();
System.out.println("动物名:" + form.getOname());
System.out.println("数量:" + form.getOcount());
System.out.println("备注:" + form.getMemo());
if(result.hasErrors()){
model.addObject("MSG", "出错啦!");
}else{
model.addObject("MSG", "提交成功!");
}
model.setViewName("zoolist");
return model;
}
(2)
@RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)
public ModelAndView doAdd(ModelAndView model, @Valid AnimalForm form, BindingResult result){
System.out.println("动物名:" + form.getOname());
System.out.println("数量:" + form.getOcount());
System.out.println("备注:" + form.getMemo());
if(result.hasErrors()){
model.addObject("MSG", "出错啦!");
}else{
model.addObject("MSG", "提交成功!");
}
model.setViewName("zoolist");
return model;
}
(3)
@RequestMapping(path = "/list", params = {"save"}, method = RequestMethod.POST)
public String doAdd(@Valid AnimalForm form, BindingResult result){
System.out.println("动物名:" + form.getOname());
System.out.println("数量:" + form.getOcount());
System.out.println("备注:" + form.getMemo()); return "zoolist";
}
注意(1)(2)仅仅是ModelAndView实例化的方式不同而已,一个是自己手动实例化,一个是框架实例化;
(3)中我们去掉了model参数,但这并不影响我们的验证以及将验证结果传递到页面,只不过是你不能通过model设置一些attribute到页面了。
这里说一下Model和ModelAndView的区别:
Model主要用于将数据传递到页面,一般采用model.addAttribute("key", object)的方式,页面通过各种表达式将其显示出来;
ModelAndView有两个作用,一个是上面Model的作用;另一个就是可以设置view,也就是跳转方向,view既可以是字符串,也可以是View类型的object。
三、添加代码到zoolist.html
<div th:text="${MSG}">这里是信息提示.</div>
<br>
<div th:errors="${animalForm.oname}"></div>
<div th:errors="${animalForm.ocount}"></div>
<div th:errors="${animalForm.memo}"></div>
完整的内容:
<!DOCTYPE html>
<html xmlns:th="http://www.thymeleaf.org">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>zoo list</title>
</head>
<body>
<a href='.'>首页</a>->动物列表
<br><br>
<div th:text="${MSG}">这里是信息提示.</div>
<br>
<div th:errors="${animalForm.oname}"></div>
<div th:errors="${animalForm.ocount}"></div>
<div th:errors="${animalForm.memo}"></div>
<br>
<form id="iform" th:action="@{/list.html?save}" th:method="post" th:object="${animalForm}">
<table border="1">
<tr>
<th>动物名称</th>
<th>数量</th>
<th>备注</th>
<th>Action</th>
</tr>
<tr>
<td><input type="text" name="oname" value="" th:value="*{oname}"/></td>
<td><input type="text" name="ocount" value="" th:value="*{ocount}"/></td>
<td><input type="text" name="memo" value="" th:value="*{memo}"/></td>
<td><input type="submit" value="添加"/></td>
</tr>
</table>
</form>
<hr>
<table border="1">
<tr>
<th>序号</th>
<th>动物名称</th>
<th>数量</th>
<th>备注</th>
</tr>
<tr>
<td>1</td>
<td>大马猴</td>
<td>10</td>
<td>机灵古怪,俏皮活泼</td>
</tr>
<tr>
<td>2</td>
<td>大熊猫</td>
<td>80</td>
<td>体型笨重,喜欢吃竹子</td>
</tr>
<tr>
<td>3</td>
<td>澳洲羊驼</td>
<td>13</td>
<td>长相奇特,大国人俗称其草泥马</td>
</tr>
<tr>
<td>4</td>
<td>峨眉山猴</td>
<td>90</td>
<td>不怕人,有时候发贱抢游客面包吃</td>
</tr>
</table>
</body>
</html>
解释:
<div th:text="${MSG}">这里是信息提示.</div>,还记得前面controller的代码中我们在返回页面的model中放了一个attribute名字叫“MSG”么,对了,在这里我们就可以通过表达式th:text="${MSG}"取得其值了。thymeleaf解析这个标签的时候会将“这里是信息提示.”这个字符串替换成“MSG”对应的内容。
<div th:errors="${animalForm.oname}"></div>,thymeleaf使用th:errors表达式可以取得错误信息的内容,${animalForm.oname}表示取得animalForm里oname字段的错误信息;如果验证oname字段时出现错误,那么在这个div里面会显示出该错误信息。
关于thymeleaf的validate表达式详细介绍请参阅 http://www.thymeleaf.org/doc/tutorials/3.0/thymeleafspring.html#validation-and-error-messages
好了,我们该修改的都完成了,重启tomcat进入浏览器吧,我的效果如下:
验证有错误的情况:
验证通过的效果:
好啦,如果你也做到这个样子,也就算达到本篇的目的了,这个页面虽然很简陋,用的也都是静态数据,但这基本上展示了如何使用@valid做form验证。
关于验证改进:
对于oname:我们可以换成@NotBlank注解,它可以将全是空格的字符串作为空字符串,和@NotEmpty不同的是,@NotBlank只可用于CharSequence类型,并检查该元素是否为null或者该元素经过trim之后的长度是否为0。
对于ocount:我们把验证代码修改为:
@NotBlank(message="数量: 不能为空")
@Pattern(regexp="[1-9]{1,3}", message="数量X: 必须为正整数,并且0<X<1000")
private String ocount;
@Pattern,一看便知这是用正则表达式做检查,[1-9]{1,3}表示三位正整数并且要大于0。
如果你对正则表达式还不了解,可以参阅javaSE的api文档:http://docs.oracle.com/javase/8/docs/api/java/util/regex/Pattern.html
对于一些特殊的验证,你可以定义自己的验证类,既可以实现springMVC的validator接口,也可以实现JSR303的注解约束方式验证,这里采用JSR303方式。
1, 创建package:com.zoo.constraint,以及com.zoo.constraint.impl。
2, 在com.zoo.constraint中定义Annotation类型Memo:
package com.zoo.constraint; import static java.lang.annotation.ElementType.FIELD;
import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention;
import java.lang.annotation.Target; import javax.validation.Constraint;
import javax.validation.Payload; import com.zoo.constraint.impl.MemoValidator; @Retention(RUNTIME)
@Target({ FIELD, METHOD })
@Constraint(validatedBy=MemoValidator.class)
public @interface Memo { String message() default "请输入正确的备注"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {};
}
3, 在com.zoo.constraint.impl中定义类MemoValidator:
package com.zoo.constraint.impl; import java.util.HashSet; import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext; import com.zoo.constraint.Memo; public class MemoValidator implements ConstraintValidator<Memo, String> { @Override
public void initialize(Memo arg0) {
} @Override
public boolean isValid(String arg0, ConstraintValidatorContext arg1) {
HashSet<String> memoSet = new HashSet<String>();
memoSet.add("圈养");
memoSet.add("散养");
return memoSet.contains(arg0);
} }
4, 修改AnimalForm类:
package com.zoo.web.form; import javax.validation.constraints.Pattern; import org.hibernate.validator.constraints.NotBlank; import com.zoo.constraint.Memo; public class AnimalForm { private long id; @NotBlank(message="动物名: 不能为空")
private String oname; @Pattern(regexp="[1-9]{1,3}", message="数量X不能为空,必须为正整数,并且0<X<1000")
private String ocount; @Memo(message = "备注不能为空,且只能填写\"圈养\",或者\"散养\"")
private String memo; /** getters and setters **/
}
ok!重启tomcat,进入浏览器,在三个字段都不输入的情况下,我的界面如下:
本篇就此结束,是不是很简单呢,这里只是抛砖引玉做一个极简的介绍,如需深入了解请参阅官方文档。
下一篇是数据持久化之保存在MySQL数据库。
SpringMVC4+thymeleaf3的一个简单实例(篇四:form表单数据验证)的更多相关文章
- SpringMVC4+thymeleaf3的一个简单实例(篇一:基本环境)
首语:用SpringMVC和thymeleaf实现一个简单的应用,包括基本环境搭建,SpringMVC4和thymeleaf3的整合,页面参数的获取,页面参数验证,以及用MySQL保存数据.我会把步骤 ...
- SpringMVC4+thymeleaf3的一个简单实例(篇五:页面和MySql的数据交互-展示以及存储)
这一篇将介绍怎样把页面数据保存的MySQL数据库,并将数据库内容展示到页面上.首先做一个基础工作,添加以下jar到lib:1: mysql-connector-Java-5.1.40-bin.jar ...
- SpringMVC4+thymeleaf3的一个简单实例(篇三:页面参数获取)
本篇将通过示例介绍页面参数是如何传递到后台的.我们继续沿用之前搭好的程序结构,如果你不知道,请参照前两篇.为方便跳转页面,我们在首页以及zoolist.html页面都加上彼此地址的链接:首页: zoo ...
- SpringMVC4+thymeleaf3的一个简单实例(篇二:springMVC与thymeleaf的整合)
延续前篇内容. 开始之前,我们首先要准备以下12个jar文件:spring-aop-4.3.3.RELEASE.jarspring-beans-4.3.3.RELEASE.jarspring-cont ...
- C++ 容器的综合应用的一个简单实例——文本查询程序
C++ 容器的综合应用的一个简单实例——文本查询程序 [0. 需求] 最近在粗略学习<C++ Primer 4th>的容器内容,关联容器的章节末尾有个很不错的实例.通过实现一个简单的文本查 ...
- Ajax实现局部数据交互的一个简单实例
想要实现的功能:利用Ajax技术通过点击一个<button>按钮,然后在指定的文本框中输出想要的值. 1.使用Jsp创建一个前端页面. <body> <div style ...
- MVC的一个简单实例
基本思路: 一个Regist.jsp注册页面,用于收集用户信息,发送请求给控制器Servlet:控制器层Servlet封装模型层对象 jBean,并调用其方法regiser实现用户信息的保存:模型层J ...
- Win32 API 多线程编程——一个简单实例(含消息参数传递)
Win32 API进行程序设计具有很多优点:应用程序执行代码小,运行效率高,但是他要求程序员编写的代码较多,且需要管理所有系统提供给程序的资源,要求程序员对Windows系统内核有一定的了解,会占用程 ...
- IoC容器Autofac正篇之简单实例(四)
先上一段代码. namespace ConsoleApplication3 { class Program { static void Main(string[] args) { ContainerB ...
随机推荐
- excel小技巧-用于测试用例的编号栏:“获取当前单元格的上一格的值+1”=INDIRECT(ADDRESS(ROW()-1,COLUMN()))+1
编写用例的时候使用,经常修改用例的时候会需要增加.删除.修改条目,如果用下拉更新数值的方式会很麻烦. 1.使用ctrl下拉,增删移动用例的时候,需要每次都去拉,万一列表比较长,会很麻烦 2.使用ROW ...
- 一个简单的GI--Reflective Shadow Maps
Reflective Shadow Maps是一种简单的GI,虽然名字带有Shadow Maps,但是和Shadow Maps没有什么关系,是在screen space的一种方法,不像光线追踪和辐射度方 ...
- RC滤波器
1.一阶RC低通滤波器 RC低通滤波器的电路及其幅频.相频特性如下图所示,输入电压为ex,输出电压为ey 其中ζ=RC,截止频率f=1/2πRC. 2.一阶RC高通滤波器 其中ζ=RC,截止频率f=1 ...
- del重复数
楼主 发表于: 2010-06-21 11:46:31 本帖最后由 luckycynthia 于 2010-06-21 11:47:46 编辑 在抓取数据后对数据进行操作的途中,有时候会碰到重复数据, ...
- ZOJ Problem Set - 3758 素数
Singles' Day Time Limit: 2 Seconds Memory Limit: 65536 KB Singles' Day(or One's Day), an unofficial ...
- Cogs 1672. [SPOJ375 QTREE]难存的情缘 LCT,树链剖分,填坑计划
题目:http://cojs.tk/cogs/problem/problem.php?pid=1672 1672. [SPOJ375 QTREE]难存的情缘 ★★★☆ 输入文件:qtree.in ...
- SRM 410(1-250pt, 1-500pt)
DIV1 250pt 题意:对于图G,有一些点和边,点中有一些点称为特殊点.问在所有特殊点最终不能处于同一个联通块的条件下,最多能给在图G中添加多少条边. 解法:首先,对于图G,处理出它有哪些联通块, ...
- The Doors - POJ 1556 (线段相交)
题目大意:有一个房间(左上角(0,10),右下角(10,0)),然后房间里有N面墙,每面墙上都有两个门,求出来从初始点(0,5),到达终点(10,5)的最短距离. 分析:很明显根据两点之间直线最短 ...
- 常用shell变量$#,$@,$0,$1,$2等
常用shell变量$#,$@,$0,$1,$2的含义解释: $$ Shell本身的PID(ProcessID) $! Shell最后运行的后台Process的PID $? 最后运行的命令的结束代码(返 ...
- Java多线程实现简单的售票程序
设计一个多线程程序如下:设计一个火车售票模拟程序.假如火车站要有100张火车票要卖出,现在有5个售票点同时售票,用5个线程模拟这5个售票点的售票情况 1.要求打印出每个售票点所卖出的票号 2.各售票点 ...