<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
@Data
@Accessors(chain = true)
@TableName("user")
@Validated
public class User {
private Integer id;
@NotBlank(message = "姓名不能为空")
private String name;
@Min(message = "年龄不正确1-100之间",value = 1)
@Max(message = "年龄不正确1-100之间",value = 1)
private Integer age ;
private Integer sex;
}
通常情况下,在javax.validation.constraints 包下,定义了一系列的约束(constraint)注解,一共 22 个注解,快速略过即可。如下:
@NotBlank``:只能用于字符串不为 null ,并且字符串 .trim() 以后 length 要大于 0 。
@NotEmpty:集合对象的元素不为 0 ,即集合不为空 。
@NotNull:不能为 null 。
@Null`:必须为 null 。
@DecimalMax(value)
:被注释的元素必须是一个数字,其值必须小于等于指定的最大值。
@DecimalMin(value)
:被注释的元素必须是一个数字,其值必须大于等于指定的最小值。
@Digits(integer, fraction)
:被注释的元素必须是一个数字,其值必须在可接受的范围内。
@Positive
:判断正数。
@PositiveOrZero
:判断正数或 0 。
@Max(value)
:该字段的值只能小于或等于该值。
@Min(value)
:该字段的值只能大于或等于该值。
@Negative
:判断负数。
@NegativeOrZero
:判断负数或 0 。
@AssertFalse
:被注释的元素必须为 true 。
@AssertTrue
:被注释的元素必须为 false 。
@Size(max, min):检查字段的 size 是否在 min 和 max 之间,可以是字符串、数组、集合、Map 等。
@Future:被注释的元素必须是一个将来的日期。
@FutureOrPresent:判断日期是否是将来或现在日期。
@Past:检查该字段的日期是在过去。
@PastOrPresent:判断日期是否是过去或现在日期。
@Email:被注释的元素必须是电子邮箱地址。
@Pattern(value):被注释的元素必须符合指定的正则表达式。
Hibernate Validator 附加的约束注解,在org.hibernate.validator.constraints 包下,定义了一系列的约束(constraint)注解。常见的如示。
@Range(min=, max=):被注释的元素必须在合适的范围内。
@Length(min=, max=):被注释的字符串的大小必须在指定的范围内。
@URL(protocol=,host=,port=,regexp=,flags=):被注释的字符串必须是一个有效的 URL 。
@SafeHtml:判断提交的 HTML 是否安全。例如说,不能包含 javascript 脚本等等。
其他的就不一一列举了,有感兴趣的小伙伴可以去源码包看看。
@Valid和 @Validated
@Valid 注解,是 Bean Validation 所定义,可以添加在普通方法、构造方法、方法参数、方法返回、成员变量上,表示它们需要进行约束校验。
@Validated 注解,是 Spring Validation 锁定义,可以添加在类、方法参数、普通方法上,表示它们需要进行约束校验。同时,@Validated 有 value 属性,支持分组校验。
对于初学者来说,很容易搞混 @Valid 和 @Validated 注解。
① 声明式校验:Spring Validation 仅对 @Validated 注解,实现声明式校验。
② 分组校验:Bean Validation 提供的 @Valid注解,因为没有分组校验的属性,所以无法提供分组校验。此时,我们只能使用 @Validated 注解。
③ 嵌套校验:相比来说,@Valid注解的地方,多了【成员变量】。这就导致,如果有嵌套对象的时候,只能使用@Valid注解。
虽然 JSR303 和 Hibernate Validtor 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要 自定义校验注解。
下面以“List数组中不能含有null元素”为实例自定义校验注解
1、注解定义如示。
@Target({ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD})
@Retention(RUNTIME)
@Documented
@Constraint(validatedBy = ListNotHaveNullValidatorImpl.class)//此处指定了注解的实现类
public @interface ListNotHaveNull {
/**
* 添加value属性,可以作为校验时的条件,若不需要,可去掉此处定义
*/
int value() default 0;
String message() default "List集合中不能含有null元素";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
/**
* 定义List,为了让Bean的一个属性上可以添加多套规则
*/
@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER})
@Retention(RUNTIME)
@Documented
@interface List {
ListNotHaveNull[] value();
}
}
2、编写自定义校验实现类
@Service
public class ListNotHaveNullValidatorImpl implements ConstraintValidator<ListNotHaveNull, List> {
private int value;
@Override
public void initialize(ListNotHaveNull constraintAnnotation) {
//传入value 值,可以在校验中使用
this.value = constraintAnnotation.value();
}
public boolean isValid(List list, ConstraintValidatorContext constraintValidatorContext) {
for (Object object : list) {
if (object == null) {
//如果List集合中含有Null元素,校验失败
return false;
} else if (object instanceof String) {
String value = object.toString();
if (value.trim().length() == 0){
return false;
}
}
}
return true;
}
}
3、model中添加注解:
@Data
public class SysRole {
private Long roleId;
@NotBlank(message = "角色名不能为空")
private String name;
@NotEmpty(message = "资源列表不能为空")
@ListNotHaveNull(message = "List 中不能含有null元素")
@Valid
private List<String> paths;
}
4、编写前端控制器
@PostMapping("/addRole")
public R addRole(@RequestBody @Valid SysRole sysRole) {
System.out.println("走到这里说明校验成功");
System.out.println(sysRole);
return R.ok(R.SUCCESS_MSG);
}
使用方法同 “简单校验”,在在需要校验的Model上面加上@Valid即可。
public class ValidatorUtils {
private ValidatorUtils() { }
private static Validator validator;
static {
validator = Validation.buildDefaultValidatorFactory().getValidator();
}
/**
* 校验对象
*
* @param object 待校验对象
* @param groups 待校验的组
* @throws RRException 校验不通过,则报RRException异常
*/
public static void validateEntity(Object object, Class<?>... groups) throws RRException {
Set<ConstraintViolation<Object>> constraintViolations = validator.validate(object, groups);
if (!constraintViolations.isEmpty()) {
Iterator<ConstraintViolation<Object>> iterator = constraintViolations.iterator();
StringBuilder msg = new StringBuilder();
while (iterator.hasNext()) {
ConstraintViolation<Object> constraint = iterator.next();
msg.append(constraint.getMessage()).append(',');
}
throw new RRException(msg.toString().substring(0,msg.toString().lastIndexOf(',')));
}
}
}
使用方式,在接收到前端传递的参数后,使用ValidatorUtils.validateEntity(【参数名】);即可校验,支持分组校验,分组需要定义分组接口。
这是因为 @RequestBody @Valid 抛出的异常没有被捕获,所以需要建立一个全局自定义异常处理类
@RestControllerAdvice
public class GlobalExceptionHandler {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
@ResponseBody
public Result MethodArgumentNotValidExceptionHandler(MethodArgumentNotValidException e)
{
//获取实体类定义的校验注解字段上的message作为异常信息,@NotBlank(message = "用户密码不能为空!")异常信息即为"用户密码不能为空!"
List<ObjectError> allErrors = e.getBindingResult().getAllErrors();
String message = allErrors.stream().map(s -> s.getDefaultMessage()).collect(Collectors.joining(";"));
List<String> msgList = new ArrayList<>();
msgList = Arrays.asList(message.split(";"));
return Result.failed(msgList.get(0));
}
}
这种情况下,使用@Valid
和@Validated
都可以。
requestParam/PathVariable
参数校验
GET
请求一般会使用requestParam
或PathVariable
传参。如果参数比较多(比如超过6个),还是推荐使用DTO对象接收。否则,推荐将一个个参数平铺到方法入参中。在这种情况下,必须在Controller类上标注@Validated
注解,并在入参上声明约束注解(如@Min等)。如果校验失败,会抛出ConstraintViolationException
异常。代码示例如下:
1、Controller类上标注@Validated
注解
2、全局捕获该异常
@ExceptionHandler({ConstraintViolationException.class})
@ResponseBody
public Result handleConstraintViolationException(ConstraintViolationException ex) {
return Result.failed(ex.getMessage());
}
3、如何GET
请求requestParam
则@RequestParam(value = “customerId”) 不能添加该注解
4、如何GET
请求PathVariable
则需加上@PathVariable()