JSR 303 是Java 为Bean 数据合法性校验提供的标准框架,它已经包含在JavaEE 中,叫做Bean Validation,用来对参数的校验。
JSR 303 通过在Bean 属性上标注类似于@NotNull、@Max 等标准的注解指定校验规则,并通过标准的验证接口对Bean 进行验证。
JSR 303 只是一个规范,我们不能直接使用。在规范的实现中我们常用的就是: Hibernate-Validator。
@NotEmpty
private String name;
//@NotEmpty 表示name不能为空
//Asserts that the annotated string, collection, map or array is not {@code null} or empty.
//该注解应用于字符串、集合、数组等等不为空,集合长度必须大于0
@Range(min = 1,max = 100)
private Integer age;
//数据范围在1-100之间
/**
* 编写方法,处理添加妖怪
* 1. springmvc可以将提交的数据,按照参数名和对象的属性名匹配
* 2. 直接封装到对象中
* String => Integer
* 3. @Valid Monster monster :表示对monster接收的数据进行校验
* 4. Errors errors 表示如果校验出现错误,将校验的错误信息保存 errors
* 5. Map map 表示如果校验出现错误, 将校验的错误信息保存 map 同时保存monster对象
* 6. 校验发生的时机: 在springmvc底层,反射调用目标方法时,会接收到http请求的数据,然后根据注解来进行验证
* , 在验证过程中,如果出现了错误,就把错误信息填充到errors 和 map
*/
@RequestMapping(value = "/save")
public String save(@Valid Monster monster, Errors errors, Map<String, Object> map) {
System.out.println("----monster---" + monster);
//我们为了看到验证的情况,我们输出map 和 errors
System.out.println("===== map ======");
for (Map.Entry<String, Object> entry : map.entrySet()) {
System.out.println("key= " + entry.getKey() + " value=" + entry.getValue());
}
System.out.println("===== errors ======");
if (errors.hasErrors()) {//判断是否有错误
List<ObjectError> allErrors = errors.getAllErrors();
for (ObjectError error : allErrors) {
System.out.println("error=" + error);
}
return "datavalid/monster_addUI";
}
return "datavalid/success";
}
自己输入一个不符合的值。
=====map=====
key=monster value=Monster{id=null, email='jack@sohu.com', age=900, name='',
birthday=Thu Nov 11 00:00:00 CST 1999, salary=11.11}
key=org.springframework.validation.BindingResult.monster
value=org.springframework.validation.BeanPropertyBindingResult: 2 errors
Field error in object 'monster' on field 'age': rejected value [900]; codes
[Range.monster.age,Range.age,Range.java.lang.Integer,Range]; arguments
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[monster.age,age]; arguments []; default message [age],100,1]; default message
[需要在1 和100 之间]
Field error in object 'monster' on field 'name': rejected value []; codes
[NotEmpty.monster.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[monster.name,name]; arguments []; default message [name]]; default message
[不能为空]
=====errors=====
验证出现错误
验证错误=Field error in object 'monster' on field 'age': rejected value [900]; codes
[Range.monster.age,Range.age,Range.java.lang.Integer,Range]; arguments
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[monster.age,age]; arguments []; default message [age],100,1]; default message
[需要在1 和100 之间]
验证错误=Field error in object 'monster' on field 'name': rejected value []; codes
[NotEmpty.monster.name,NotEmpty.name,NotEmpty.java.lang.String,NotEmpty];
arguments
[org.springframework.context.support.DefaultMessageSourceResolvable: codes
[monster.name,name]; arguments []; default message [name]]; default message
[不能为空]
<bean id="messageSource" class=
"org.springframework.context.support.ResourceBundleMessageSource">
<property name="basename" value="i18n">property>
bean>
NotEmpty.monster.name=\u7528\u6237\u540d\u4e0d\u80fd\u4e3a\u7a7a
typeMismatch.monster.age=\u5e74\u9f84\u8981\u6c42\u5728\u0031\u002d\u0031\u0035\u0030\u4e4b\u95f4
typeMismatch.monster.birthday=\u751f\u65e5\u683c\u5f0f\u4e0d\u6b63\u786e
typeMismatch.monster.salary=\u85aa\u6c34\u683c\u5f0f\u4e0d\u6b63\u786e
<%@ taglib prefix="form" uri="http://www.springframework.org/tags/form" %>
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
添加妖怪
添加妖怪
妖怪名字:
妖怪年龄~:
电子邮件:
妖怪生日: 要求以"9999-11-11"的形式
妖怪薪水: 要求以"123,890.12"的形式
查看源码可以知道: @NotNull * The annotated element must not be {@code null}.*Accepts any type.
如果是字符串验证空, 建议使用@NotEmpty.
这显然不符合我们的要求。
使用@NotNull + @Range 组合使用解决.
修改Monster
public class Monster {
private Integer id;
//email是string,使用@NotEmpty
@NotEmpty
private String email;
//@Range(min = 1,max = 100)
//表示接收的age值,在 1-100之间
@NotNull(message = "age不能为空")
@Range(min = 1,max = 100)
private Integer age;
//@NotEmpty 表示name不能为空
//Asserts that the annotated string, collection, map or array is not {@code null} or empty.
@NotEmpty
private String name;
@NotNull(message = "生日不能为空")
@DateTimeFormat(pattern = "yyyy-MM-dd")
private Date birthday;
@NotNull(message = "薪水不能为空")
@NumberFormat(pattern = "###,###.##")
private Float salary;
}
这时age 不能为空,同时必须是1-100, (也不能输入haha, hello 等不能转成数字的内容)。
图例Spring MVC 通过反射机制对目标方法进行解析,将请求消息绑定到处理方法的入参中。
数据绑定的核心部件是DataBinder,运行机制如下:
从serverRequest拿到前端的数据,将拿到处理方法的信息使用DateBinder的内置转换器进行数据类型转换/格式化。
在数据类型转换过程中,如果发生不能转换的错误就直接放到BindingResult中。
然后我们再进行数据校验,如果出错了就继续放入BindingResult接收。
(1)@Valid 和 @Validated 两者都可以对数据进行校验,待校验字段上打的规则注解(@NotNull, @NotEmpty等)都可以对 @Valid 和 @Validated 生效;
(2)@Valid 进行校验的时候,需要用 BindingResult 来做一个校验结果接收。当校验不通过的时候,如果不手动return ,就不会阻止程序的执行;
(3)@Validated 进行校验的时候,当校验不通过的时候,程序会抛出400异常,阻止方法中的代码执行,这时需要再写一个全局校验异常捕获处理类,然后返回校验提示。
(4)总体来说,@Validated 使用起来要比 @Valid 方便一些,它可以帮我们节省一定的代码,并且使得方法看上去更加的简洁。
在默认情况下,表单提交的数据都会和pojo 类型的javabean 属性绑定,如果程序员在开发中,希望取消某个属性的绑定,也就是说,不希望接收到某个表单对应的属性的值,则可以通过@InitBinder 注解取消绑定.
修改MonsterHandler.java , 增加方法
//取消绑定 monster的name表单提交的值给monster.name属性
@InitBinder
public void initBinder(WebDataBinder webDataBinder) {
/**
* 1. 方法上需要标注 @InitBinder springmvc底层会初始化 WebDataBinder
* 2. 调用 webDataBinder.setDisallowedFields("name") 表示取消指定属性的绑定
* 即:当表单提交字段为 name时, 就不在把接收到的name值,填充到model数据monster的name属性
* 3. 机制:springmvc 在底层通过反射调用目标方法时, 接收到http请求的参数和值,使用反射+注解技术
* 取消对指定属性的填充
* 4. setDisallowedFields支持可变参数,可以填写多个字段
* 5. 如果我们取消某个属性绑定,验证就没有意义了,应当把验证的注解去掉, name属性会使用默认值null
*/
webDataBinder.setDisallowedFields("name");
}
修改Monster.java
//如果希望取消某个属性绑定,验证就没有意义了,应当把验证的注解去掉, name属性会使用默认值null
//@NotEmpty
private String name;
完成测试(页面测试)。