最近在网易云课堂上看一些视频,给大家推荐一个讲Spring Boot的视频https://study.163.com/course/courseMain.htm?courseId=1005213034,老师讲的很不错。在学习的时候我也会做一些笔记,方便日后巩固。
对这个系列感兴趣的可以看我之前写的博客:
对客户端传入数据的校验
原则
- 不要相信前端传过来的数据
- 不能相信前端传过来的任何数据
- 绝不要相信前端传过来的任何数据
- 尽量要前端少传递数据
一般来说,我们要对传进来的数据做校验,有一种写法是:
@PostMapping
public TvSeriesDto insertOne(@RequestBody TvSeriesDto tvSeriesDto) {
if (tvSeriesDto == null) {
throw new RuntimeException("参数tvSeriesDto不可为空");
}
if (tvSeriesDto.getName() == null) {
throw new RuntimeException("电视剧名字不可为空");
}
…
}
这样写是没有什么问题的,但是这样写占了很多行代码,大部分判断的条件也十分单一。这时候就需要用到Bean Vlidation。
Bean Vlidation有两种规范
- JSR:定义了注解和接口等一些标准,具体实现需要靠其他的厂家来做一些框架
- Hibernate Validator:这就是上面所说的其中一种框架,在Spring Boot中可以直接用
Bean Validation注解
- @Null 验证对象是否为空
- @NotNull 验证对象是否为非空
- @Min 验证 Number 和 String 对象是否大等于指定的值
- @Max 验证 Number 和 String 对象是否小等于指定的值
- @Size 验证对象(Array,Collection,Map,String)长度是否在给定的范围之内
- @Past 验证 Date 和 Calendar 对象是否在当前时间之前
- @Future 验证 Date 和 Calendar 对象是否在当前时间之后
- @AssertTrue 验证 Boolean 对象是否为 true
- @AssertFalse 验证 Boolean 对象是否为 false
- @Valid 级联验证注解,即这个bean里面的值也需要验证
讲了这么多注解,现在就来实际操作一下!
-
我们要给电视连续剧(TvSeries)这个类加一个属性,就是它的人物。这是一个新的类,代码如下:
package cn.luxiaofen.test; import javax.validation.constraints.NotNull; public class TvCharacterDto { private int id; private int tvSeriesId; @NotNull private String name;//这里规定name不能为null //setter,getter方法 public int getId() { return id; } public void setId(int id) { this.id = id; } public int getTvSeriesId() { return tvSeriesId; } public void setTvSeriesId(int tvSeriesId) { this.tvSeriesId = tvSeriesId; } public String getName() { return name; } public void setName(String name) { this.name = name; } }
-
对TvSeries的field做一些修改,这里不要忘了添加对应的setter和getter方法,不然Request Body里面的参数是传不进来的:
@Null private Integer id; @NotNull private String name; @DecimalMin("1") private int seasonCount; @JsonFormat(timezone="GMT+8", pattern="yyyy-MM-dd") @Past private Date originalRelease; //首发日期 //@Valid表示要级联校验;@Size(min=2)表示这个列表至少要有2项内容,否则通不过校验 @Valid @Size(min=2) @NotNull private List<TvCharacterDto> tvCharacters;
注意:在学习十一课时的时候遇到了一些小问题,TvCharacterDto这个类的id一开始被设置为了Integer型,这是因为之后可能需要加上@Null注解,而int是基本数据类型,不会为null。然而这样设置以后,如果在上传的JSON格式中省略了对id这个参数的声明,系统会抛出JsonMappingException。这是因为Integer类型默认为null,在JSON格式中无法转换,需要加@JSONignore注解。但是加了这个注解,以后在JSON中声明这个field也无效了。将类型改为int后问题可以解决。
-
下面这一步很重要!为了在使用方法的时候可以让我们的注解起作用,一定要在传入的参数上加上@Valid注解:
/** * * @param tvSeriesDto @RequestBody这个注解表明上传数据储存在RequestBody中, * 用JSON格式表示了一个TvSeriesDto对象。@Valid注解在这里 * 很重要,它代表了要去校验TvSeriesDto这个类里面的field * * */ @PostMapping public TvSeriesDto insertOne(@Valid @RequestBody TvSeriesDto tvSeriesDto) { if (log.isTraceEnabled()) { log.trace("传进来的参数是:"+tvSeriesDto); } tvSeriesDto.setId(9999); return tvSeriesDto; }
-
去POSTMAN里面测试一下这个方法,这里要注意JSON的格式:
课时11.1
深入学习Bean Validation
这里对上文提到的一些注解做了分类:
课时12.1
注解的位置
- Field level 成员变量,之前我们加的注解都是加在成员变量上
- Property level get(is) 方法上,这用于验证方法的返回值
- Class level 类上,JSR标准里没有,可以自定义
下面就来看几个例子:
//images不可为null,每个元素(Image类型)需要级联验证
@NotNull
private List<@Valid Image> images;
//images长度至少为1,不可为null,每个元素(String类型)不可为null,最短长度10
@NotNull
@Size(min=1)
private List<@Size(min=10) @NotNull String> images;
@AssertTrue
public boolean isValid() {
//这里验证类的各种成员变量组合
return true;
}
注意:
约束规则对子类依旧有效。验证的框架实际上是利用了java的反射把父类的反射出来进行验证来实现这一点。
GROUPS参数
假设现在我们有以下新的需求:
- 操作分步进行 第一步,输入名字等基本信息。第二步,验证手机号码
- 分角色,不同修改权限 管理员可修改的内容比一般用户多
解决方案是使用GROUPS参数:
- 每个约束用注解都有一个groups参数
- 可接收多个class类型(必须是接口)
- 不声明groups参数是默认组javax.validation.groups.Default
- 声明了groups参数的会从Default组移除,如需加入Default组需要显示声明,例如:
@Null(groups={Default.class, Step1.class})
@Valid vs. @Validated
- @Valid是JSR标准定义的注解,只验证Default组的约束
- @Validated是spring定义的注解,可以通过参数来指定验证的组,例如:
@Validated({Step1.class, Default.class})
表示验证Step1和Default两个组的约束 - @Valid可用在成员变量上,进行级联验证;@Validated只能用在参数上,表示这个参数需要验证
BindingResult
到目前我们讲的都是不符合规则,spring就会把请求给退回去,返回一个400的Bad Request,不会调用我们的方法。如:
public Object doSomething(@Validated @RequestBody OneDto oneDto) {
// 通不过校验的参数,不会执行这个方法
}
如果我们希望验证完不论什么结果,都不给客户端返回了,而是交给程序进行处理,我们就可以在方法的参数上加一个BindingResult,校验结果会通过result参数传递进来,然后由程序根据校验结果选择执行。
public Object doSomething(@Validated @RequestBody OneDto oneDto,
BindingResult result) {
// 参数通不过校验也会进入方法执行,校验结果会通过result参数传递进来
if (result.hasErrors()){
// 没通过校验
}
}
Bean Validation 2.0中的约束用注解
13.113.2
13.3
网友评论