普通校验
(1)给Bean的属性添加注解:javax.validation.constraints.xxx
(2)在Controller方法参数上加注解:@Valid
@TableName("brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
// 品牌id
@Null(message = "新增不能指定id")
@TableId
private Long brandId;
// 品牌名
@NotBlank(message = "品牌名必须提交")
private String name;
// 品牌名
@NotEmpty
@URL
private String logo;
// 显示状态[0-不显示;1-显示]
private Integer showStatus;
// 检索首字母
@Pattern(regexp="/^[a-zA-Z]$/]", message = "第一个字符必须是字母")
private String firstLetter;
// 排序
@Min(value = 0, message = "排序字段值必须大于零")
private Integer sort;
}
// 保存
// 校验错误会被封装到BindingResult 类中,如果没有写BindingResult 参数,会自动抛出异常
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand, BindingResult result){
if(result.hasErrors()) {
Map<String, String> map = new HashMap<>();
result.getFieldErrors().forEach(item -> {
String message = item.getDefaultMessage();
String field = item.getField();
map.put(field, message);
});
// R是自定义类用来封装返回值
return R.error(400, "提交数据不合法").put("data", map);
}else {
brandService.save(brand);
return R.ok();
}
}
分组校验
复杂场景下,对于同一个字段,不同情况下的校验规则不一样,就需要用到分组校验。
(1)自定义不同组接口
(2)在校验注解中使用groups
属性指定分组类
(3)在Controller方法中使用@Validated
注解指定在这个方法中,哪个注解生效
// 新增
public interface AddGroup {
}
// 更新
public interface UpdateGroup {
}
// 品牌id
@NotNull(message = "修改必须指定品牌id", groups = {UpdateGroup.class})
@Null(message = "新增不能指定id", groups = {AddGroup.class})
@TableId
private Long brandId;
// 品牌名
@NotBlank(message = "品牌名必须提交", groups = {UpdateGroup.class, AddGroup.class})
private String name;
// 品牌logo地址
@NotEmpty
@URL
private String logo;
// 分组校验需要使用@Validated注解指定分组
@RequestMapping("/save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand, BindingResult result){
if(result.hasErrors()) {
Map<String, String> map = new HashMap<>();
result.getFieldErrors().forEach(item -> {
String message = item.getDefaultMessage();
String field = item.getField();
map.put(field, message);
});
return R.error(400, "提交数据不合法").put("data", map);
}else {
brandService.save(brand);
return R.ok();
}
}
自定义注解
引入jar:
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
JSR303规范中,注解必须要有三个属性:
// 默认错误提示信息从哪里获取,这里是从ValidationMessages.properties配置文件中获取
String message() default "{javax.validation.constraints.Min.message}";
// 分组
Class<?>[] groups() default {};
// 自定义信息
Class<? extends Payload>[] payload() default {};
必须要有如下元数据信息:
// 注解可以作用在哪些位置
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
// 该注解可以在运行时获取到
@Retention(RUNTIME)
@Documented
// 使用哪个校验器
@Constraint(
validatedBy = {}
)
自定义注解实现
(1)编写一个自定义校验注解
(2)编写一个自定义校验器 实现ConstraintValidator接口
(3)关联自定义校验器和自定义校验注解:@Constraint( validatedBy ={ListValueConstraintValidator.class} )
// 显示状态[0-不显示;1-显示]
@ListValue(vals={0, 1})
private Integer showStatus;
自定义校验注解:
@Documented
// 使用自定义校验器
@Constraint(
validatedBy = {ListValueConstraintValidator.class}
)
@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
public @interface ListValue {
// 指定在配置文件中取com.atguigu.common.valid.ListValue.message信息
String message() default "{com.atguigu.common.valid.ListValue.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// 自定义添加一个vals属性
int[] vals() default {};
}
自定义配置文件 resources/ValidationMessages.properties:
com.atguigu.common.valid.ListValue.message=the specified value must be submitted
自定义校验器:
/**
* 自定义校验器,必须实现ConstraintValidator接口
* ConstraintValidator接口后的泛型,第一个参数指定注解,第二个参数表示校验什么类型的数据
* 这是校验的是Integer字段类型,如果需要校验其他类型,需要再写一个校验器,然后用
* @Constraint注解的validatedBy 属性指定多个校验器
*/
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
private Set<Integer> set = new HashSet<>();
/**
* 初始化方法
* @param constraintAnnotation 注解下的属性详细信息
*/
@Override
public void initialize(ListValue constraintAnnotation) {
// 案例中指定showStatus字段必须取值为0或者1,所以这个数组vals就是[0, 1]
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
/**
* 判断是否检验成功
* @param value 提交给这个注解下属性字段的值(也就是提交过来的showStatus值)
* @param constraintValidatorContext
* @return
*/
@Override
public boolean isValid(Integer value, ConstraintValidatorContext constraintValidatorContext) {
// value的值是否存在set集合中
return set.contains(value);
}
}