文章目录

Spring Boot Validate

Spring Boot 支持JSR303/JSR349验证框架,通过注解实现对参数的校验,并将校验结果封装成BindingResult对象。

常用注解

下面的表格列出常用校验类及主要功能。这些注解必须配合@Valid或@Validated使用,通过这两个注解开启校验。

注 :对于长度的校验基本都支持字符串、集合、Map、数组的长度。

下面是@Valid和@Validated的区别。

注 :嵌套验证。JavaBean a中某个属性类型是JavaBean b,对a进行验证的同时验证b。

使用说明

基本使用

以新增用户为例

新建User类,为name属性添加@NotBlank注解

@Data
class User {
	private Integer id;
	@NotBlank
	private String name;
}

新增新建用户接口,添加@Validated注解开启user参数校验

@RestController
@RequestMapping("/validate")
public class ValidateController {
	@RequestMapping("/addUser")
	public Integer addUser(@Validated User user) {
		// 假设插入数据库后,生成id为89757
		user.setId(89757);
		// 插入成功后返回id
		return user.getId();
	}
}

启动服务,测试代码

浏览器输入 :http://localhost:8080/validate/addUser
由于name是null,校验不通过,返回错误

浏览器输入 :http://localhost:8080/validate/addUser?name=ly
校验通过,返回89757

实现分组校验

同一个JavaBean在不同的场景可能需要不用的校验规则,例如:添加用户时id必须是null,修改用户时id不能是null。每一个校验注解都有group属性,通过group属性可以实现不同方法采用不同的校验规则。以新建用户为例

修改User类,添加并指定校验组

@Data
class User {
	// 定义新增校验组和更新校验组
	public interface Add {}
	public interface Update {}

	// 添加校验并指定不同的校验组
	@Null(groups = Add.class)
	@NotNull(groups = Update.class)
	private Integer id;

	// 添加新增和修改校验组
	@NotBlank(groups = {Add.class, Update.class})
	private String name;
}

添加修改用户接口,为新增和修改用户接口指定校验组

@RestController
@RequestMapping("/validate")
public class ValidateController {
	// 通过group属性指定新增用户校验组
	@RequestMapping("/addUser")
	public Integer addUser(@Validated(User.Add.class) User user) {
		// 同上,略
	}

	// 通过group属性执行修改用户校验组
	@RequestMapping("/updateUser")
	public boolean updateUser(@Validated(User.Update.class) User user) {
		// 假设修改成功,返回true
		return true;
	}
}

启动服务,测试代码

浏览器输入 :http://localhost:8080/validate/addUser?id=1&name=ly
由于id不是空,校验不通过,返回错误

浏览器输入 :http://localhost:8080/validate/addUser?name=ly
校验通过,返回89757

浏览器输入 :http://localhost:8080/validate/updateUser?name=ly
由于id是空,校验不通过,返回错误

浏览器输入 :http://localhost:8080/validate/updateUser?id=1&name=ly
校验通过,返回true

处理校验结果

Spring Boot 会将校验结果封装成BindingResult对象,可以通过BindingResult参数获取校验结果。

新增方法,用于测试校验结果的获取

@RestController
@RequestMapping("/validate")
public class ValidateController {

	// 其他方法略

	@RequestMapping("/handlerBindingResult")
	public Object bindingResult(@Validated(User.Add.class) User user, BindingResult bindingResult) {
		// 若存在校验异常则处理
		if (bindingResult.hasErrors()) {
			StringBuilder errorMessage = new StringBuilder();
			// 遍历全部校验异常并拼接异常信息
			for (FieldError error : bindingResult.getFieldErrors()) {
				errorMessage.append(error.getField()).append(error.getDefaultMessage()).append('\n');
			}
			return errorMessage.toString();
		}
		return true;
	}
}

启动服务,测试代码

浏览器输入 :http://localhost:8080/validate/handlerBindingResult?id=1
返回处理后的错误信息 :id必须为null name不能为空

浏览器输入 :http://localhost:8080/validate/handlerBindingResult?name=ly
校验通过,返回true

注 :若方法需要校验多个参数,例如:method(param1,param2…),需要依次获取校验结果,例如 :method(param1,bindingResult1,param2,bindingResult2…)。对于未通过参数注入处理的BindingResult对象,会抛出异常,可以通过捕获MethodArgumentNotValidExceptionBindException统一处理,个人习惯在这里处理。

自定义校验注解

当现有的校验注解不满足业务要求的时候,就需要通过自定义注解来满足我们自定义的校验规则。以性别校验为例,假设1代表男、2代表女,输入其他都是不合法的,下面说明如何自定义性别校验注解

新建Sex注解,用于性别参数校验

@Documented
@Retention(RUNTIME)
// 指定校验类
@Constraint(validatedBy = SexValidator.class)
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@interface Sex {
	// 必须的三个属性
	String message() default "参数值不正确";
	Class<?>[] groups() default {};
	Class<? extends Payload>[] payload() default {};
	// 允许值(默认1男2女)。若使用方的性别枚举不同,可以通过该字段指定校验通过的值。
	int[] allowed() default {1, 2};
}

新增Sex注解校验类

// 校验类需要实现ConstraintValidator接口,通过泛型指定被校验注解类型和被校验字段类型
class SexValidator implements ConstraintValidator<Sex, Integer> {
	private Set<Integer> allowed = new HashSet<>(2);

	// 初始化允许校验通过的值
	@Override
	public void initialize(Sex sex) {
		for (int i : sex.allowed()) {
			allowed.add(i);
		}
	}
	// 执行校验
	@Override
	public boolean isValid(Integer value, ConstraintValidatorContext context) {
		return allowed.contains(value);
	}
}

修改User类

@Data
class User {

	// 略

	// 假设注解使用方的性别枚举分别是0未知、1男、2女
	@Sex(allowed = {0, 1, 2}, groups = {Add.class, Update.class})
	private Integer sex;
}

启动服务,测试代码

浏览器输入 :http://localhost:8080/validate/handlerBindingResult?name=ly&sex=4
返回处理后的错误信息 :sex参数值不正确

浏览器输入 :http://localhost:8080/validate/handlerBindingResult?name=ly&sex=1
校验通过,返回true

配置校验提示信息

classpath下新建ValidationMessages.properties文件

com.example.validate.sex = :incorrect parameter values

修改Sex类

@Documented
@Retention(RUNTIME)
@Constraint(validatedBy = SexValidator.class)
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER, TYPE_USE})
@interface Sex {
	// 指定配置文件中消息
	String message() default "{com.example.validate.sex}";

	//略
}

启动服务,测试代码

浏览器输入 :http://localhost:8080/validate/handlerBindingResult?name=ly&sex=4
返回处理后的错误信息 :sex:incorrect parameter values

手动进行校验

一般用不到,校验方法如下

注入校验对象,新增手动校验测试方法

@RestController
@RequestMapping("/validate")
public class ValidateController {
	// 注入校验对象。可以通过Validation.buildDefaultValidatorFactory().getValidator()获得。
	@Autowired
	private Validator validator;

	// 其他代码略

	@RequestMapping("/activeValidate")
	public String activeValidate(User user) {
		// 使用新增用户规则校验用户对象
		Set<ConstraintViolation<User>> validate = validator.validate(user, User.Add.class);
		// 处理校验结果
		StringBuilder result = new StringBuilder();
		for (ConstraintViolation<User> violation : validate) {
			result.append(violation.getPropertyPath()).append(violation.getMessage());
		}
		return result.toString();
	}
}

启动服务,测试代码

浏览器输入 :http://localhost:8080/validate/activeValidate?name=ly
返回处理后的错误信息 :sex:incorrect parameter values
10-03 19:45