文章目录
- 1、@Valid 和 @Validated 的区别
- 2、 对象校验 (最普通)
- 3、多个对象的校验
- 4、对象级联(嵌套)校验
- 4.1、配置:
- 4.2、测试:
- 4.3、分析:
- 5、方法级别的单个参数校验
- 5.1、配置:
- 5.2、测试(略):
- 6、全局异常捕获(强烈推荐):
- 6.1、测试:
1、@Valid 和 @Validated 的区别
@Validated
对@Valid 进行了二次封装,在使用上并没有区别。推荐使用 @Validated。但在分组、注解位置、嵌套验证等功能上有所不同,这里主要就这几种情况进行说明。
不同点 | @Valid | @Validated |
来源 | 是Hibernate validation 的 校验注解 | 是 Spring Validator 的校验注解,是 Hibernate validation 基础上的增加版 |
注解位置 | 用在 构造函数、方法、方法参数 和 成员属性上 | 用在 类、方法和方法参数上。 但不能用于成员属性 |
嵌套验证 | 用在级联对象的成员属性上面 | 不支持 |
分组 | 无此功能 | 提供分组功能,可以在入参验证时,根据不同的分组采用不同的验证机制 |
2、 对象校验 (最普通)
这是最普通的校验。
public class User implements Serializable {
@NotNull(message = "uid不能为空")
@Min(value = 1, message = "pid必须为正整数")
private Long uid;
@NotBlank(message = "userName不能为空")
private String userName;
@NotBlank(message = "address 不能为空")
private String address;
// getter/setter
}
@RequestMapping("/user/saveUser")
public String saveUser(@Valid User user, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
// 打印全部的错误信息
for (ObjectError error : bindingResult.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
// 返回第一条的错误信息
if (bindingResult.hasErrors()) {
String msg=bindingResult.getAllErrors().get(0).getDefaultMessage();
return msg;
}
}
测试:
运行结果:
address 不能为空
userName不能为空
3、多个对象的校验
User 对象:
上面已经有了,本处省略。
Student 对象:
public class Student implements Serializable {
@NotNull(message = "sid不能为空")
@Min(value = 1, message = "pid必须为正整数")
private Long sid;
@NotBlank(message = "sUserName不能为空")
private String sUserName;
@NotBlank(message = "sAddress 不能为空")
private String sAddress;
//getter/setter
}
Controller:
定义 user、student 两个对象和对应的 bindingResult、bindingResult2 异常对象。
/**
* 多个对象校验
* @param user
* @param bindingResult
*/
@RequestMapping("/user/saveAll")
public void saveAll(@Validated User user, BindingResult bindingResult,@Validated Student student, BindingResult bindingResult2) {
if (bindingResult.hasErrors()) {
for (ObjectError error : bindingResult.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
System.out.println("-----------");
if (bindingResult2.hasErrors()) {
for (ObjectError error : bindingResult2.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
测试数据:
uid=1111
userName=xxxx
pid=2222
sAddress=bbbbb
测试结果:
address 不能为空
-----------
sid不能为空
sUserName不能为空
4、对象级联(嵌套)校验
与对象校验 的唯一区别是 在关联对象的的 users 属性上加 @Valid
。
4.1、配置:
Item 对象:
关联的 users 属性上,必须加上 @Valid
public class Item implements Serializable {
@NotNull(message = "id不能为空")
@Min(value = 1, message = "id必须为正整数")
private Long id;
@Valid
@NotNull(message = "users 不能为空")
@Size(min = 1, message = "users 至少要有一个属性")
private List<User> users;
}
User 对象:
User 上面已经有,本处省略。
Controller:
此处 ,既可以使用 @Valid
,也可以使用 @Validated
。如果使用分组,则只能使用 @Validated
。
@RequestMapping("/user/saveItem")
public void saveItem(@RequestBody @Validated Item item, BindingResult bindingResult) {
if (bindingResult.hasErrors()) {
for (ObjectError error : bindingResult.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
4.2、测试:
测试数据:
地址: http://127.0.0.1/user/saveItem
{
"id": 0,
"users": [
{
"uid": 11111,
"userName": "北京时"
}
]
}
测试结果:
address 不能为空
id必须为正整数
4.3、分析:
从测试结果中可以看出,已经开校验 User 中 address 属性值不能为空。说明级联校验起作用了。
正确的数据:
{
"id":3333,
"users": [
{
"uid": 11111,
"userName": "北京时",
"address":"bbbb"
}
]
}
5、方法级别的单个参数校验
1、在方法所在的类上添加 @Validated
。注意,此处 只能使用 @Validated
注解 ,@Valid
无效 ,因为 @Valid
不能用在类上。
2、对方法中的每个参数上加上所需的验证注解,如 @Rang
, @Max
,@Min
、自定义注解
等注解 ;
3、定义 ConstraintViolationException 的异常拦截器 (这是系统全局 捕获异常。也可以使用BindingResult 捕获取异常,只是太繁琐了,不推荐使用)
5.1、配置:
Controller :
@Validated //第1步,告诉MethodValidationPostProcessor此Bean需要开启方法级别验证支持
@RestController
public class ValidationController {
@RequestMapping(value = "/validation/demo")
public void demo1(
@Range(min = 1, max = 9, message = "年级只能从1-9") //第2步
@RequestParam(name = "grade", required = true) int grade, //
@Min(value = 1, message = "班级最小只能1") @Max(value = 99, message = "班级最大只能99") //第2步
@RequestParam(name = "classroom", required = true) int classroom) { //
System.out.println(grade + "," + classroom);
}
}
5.2、测试(略):
6、全局异常捕获(强烈推荐):
前面的示例中,@Valid 和 @Validated 的异常信息捕获必须使用 BindingResult bindingResult
(如下示例), 非常麻烦。
public void saveAll(@Validated User user, BindingResult bindingResult){
if (bindingResult.hasErrors()) {
for (ObjectError error : bindingResult.getAllErrors()) {
....
}
}
}
下面提供一种全局的异常信息捕获方法:
使用 @ControllerAdvice
注解,具本如下:
@ControllerAdvice
@Component
public class GlobalExceptionHandler {
/**
* 拦截捕捉 MissingServletRequestParameterException 异常
*/
@ResponseBody
@ExceptionHandler(value = MissingServletRequestParameterException.class)
public String doMissingServletRequestParameterHandler(MissingServletRequestParameterException exception) {
MissingServletRequestParameterException exs = (MissingServletRequestParameterException) exception;
System.err.println(exs.getMessage());
return "bad request, ";
}
/**
* 拦截捕捉 ConstraintViolationException 异常
*/
@ResponseBody
@ExceptionHandler(value = ConstraintViolationException.class)
public Map ConstraintViolationExceptionHandler(ConstraintViolationException ex) {
Map map = new HashMap();
Set<ConstraintViolation<?>> constraintViolations = ex.getConstraintViolations();
Iterator<ConstraintViolation<?>> iterator = constraintViolations.iterator();
List<String> msgList = new ArrayList<>();
while (iterator.hasNext()) {
ConstraintViolation<?> cvl = iterator.next();
System.err.println(cvl.getMessageTemplate());
msgList.add(cvl.getMessageTemplate());
}
map.put("status", 500);
map.put("msg", msgList);
return map;
}
/**
* 拦截捕捉 MethodArgumentNotValidException 异常
*/
@ExceptionHandler(MethodArgumentNotValidException.class)
public Map doMethodArgumentNotValidException2(MethodArgumentNotValidException ex){
Map map = new HashMap();
map.put("status", 500);
map.put("msg", "请求异常");
BindingResult result = ex.getBindingResult();
List<String> msgList = new ArrayList<String>();
if (result.hasErrors()) {
List<ObjectError> errors = result.getAllErrors();
ObjectError error=errors.get(0);
msgList.add(error.getDefaultMessage());
String firstMsg=msgList.get(0);
map.put("msg", msgList);
return map ;
}
return map ;
}
/**
* 拦截捕捉 BindException 异常
*/
@ExceptionHandler(BindException.class)
@ResponseBody
public R handleBindException(BindException ex) {
List<FieldError> bindingResult = ex.getBindingResult().getFieldErrors();
List<String> msgList = new ArrayList<String>();
for (FieldError fieldError : bindingResult) {
System.err.println(fieldError.getField() + " " + fieldError.getDefaultMessage());
msgList.add(fieldError.getDefaultMessage());
}
String firstMsg = msgList.get(0);
return R.error(firstMsg);
}
}
6.1、测试:
测试数据:
grade=0
classroom=0
测试结果:
班级最小只能1
年级只能从1-9