情况1
@PostMapping
public String doIt(@RequestBody @Valid List<User> users) {
return "SUCCESS";
}
如果API这样设计的话,经测试,@Valid无法发挥作用。
情况2
@PostMapping
public String doItAgain(@RequestBody @Valid UserList userList) {
return "SUCCESS";
}
@Data
public class UserList() {
@Valid
private List<User> users;
}
这种情况是情况1的进阶,这样设计至少@Valid能够发挥作用了。但是太麻烦,需要专门写一个UserList类,简直不优雅。同时外接的JSON需要需要多套一层,如下
{"users": [{"name": "Deolin", "age": 18}, {"name": "Rin", "age": 17}]}
多一层"users"对调用方来说也是麻烦。
分析
情况1那样子设计行不通的原因是,java.util.List(ArrayList)内部通过持有一个数组来保存对象们,而作为Java官方的类,内部肯定不会在数组上声明@Valid,所以内部的对象们没有得到应有的递归校验。
所以,考虑使用一种新的java.util.List实现,来变相的达到列表校验的效果。
解决方案
/**
* 可被校验的List
*
* @param <E> 元素类型
* @author Deolin
*/
public class ValidableList<E> implements List<E> {
@Valid
private List<E> list;
public ValidableList() {
this.list = new ArrayList<>();
}
public ValidableList(List<E> list) {
this.list = list;
}
public List<E> getList() {
return list;
}
public void setList(List<E> list) {
this.list = list;
}
@Override
public int size() {
return list.size();
}
@Override
public boolean isEmpty() {
return list.isEmpty();
}
// 其他的Override方法省略。它们与size()和isEmpty()类似,直接调用private List list处理。
}
可以看到ValidableList具有二象性,ValidableList LIKE-A java.util.List,同时也是个“holder”,对外的方法全部会移交给持有的list处理。
也就是说,ValidableList与java.util.List的对外功能完全一致。
对于Spring参数绑定来说,JSON转换后也能够绑定到ValidableList对象上(就像能绑定到java.util.ArrayList对象上一样。)
实现了上述的效果后,直接在list上声明一个@Valid就解决所有问题了。
最终的解决方案
API
@PostMapping
public String noMore(@RequestBody @Valid ValidableList<User> users) {
return "SUCCESS";
}
JSON
[{"name": "Deolin", "age": 18}, {"name": "Rin", "age": 17}]