文章目录

  • 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;
	 }
}

测试:

springboot Statement 操作 HIVE_springmvc

运行结果:

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

springboot Statement 操作 HIVE_@validated_02

测试结果:

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": "北京时"
        }
    ]
}

springboot Statement 操作 HIVE_@valid_03

测试结果:

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

springboot Statement 操作 HIVE_springboot_04

测试结果:

班级最小只能1
年级只能从1-9