1.在Entity中,对需要校验的字段添加注解。
比如,字段不能为空,则对相应字段添加注解:@NotEmpty,如果需要自定义对错误的说明,则可以修改注解:@NotEmpty (message = "XX必须提交")
2.在controller中对需要校验的内容添加注解:@Valid
比如:
@RequestMapping("/save")
public R save(@Valid @RequestBody CategoryEntity category){
//进行操作
}
如想对校验结果进行记录,则有:
@RequestMapping("/save")
public R save(@Valid @RequestBody CategoryEntity category, BindingResult result){
if(result.hasErrors()){
//1.获取校验的错误结果
Map<String,String> map = new HashMap<>();
result.getFieldErrors().forEach((item)->{
String message = item.getDefaultMessage();
String field = item.getField();
map.put(field,message);
});
R.error(400,"提交的数据不合法").put("data",map);
}
categoryService.save(category);
return R.ok();
}
其中“BindingResult result“里就记录了校验结果的信息,通过getField()和getDefaultMessage()分别得到字段名和校验不通过给出的信息。
但这样每个方法都需要重复,所以可以进行统一结果处理。
3.统一异常处理
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product.controller")
public class GulimallExceptionControllerAdvice {
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleValidException(MethodArgumentNotValidException e){
log.error("数据校验出现问题:{},异常类型:{}",e.getMessage(),e.getClass());
BindingResult bindingResult = e.getBindingResult();
Map<String,String> map = new HashMap<>();
bindingResult.getFieldErrors().forEach((item)->{
map.put(item.getField(),item.getDefaultMessage());
});
return R.error(400,"数据校验出现问题").put("data",map);
}
}
其中@RestControllerAdvice是一个组合注解,由@ControllerAdvice、@ResponseBody组成。同时使用basepakage指定了处理哪里的异常。
而使用@ExceptionHandler来处理指定的异常,这里就指定了处理MethodArgumentNotValidException,就可以将前面的校验不通过的异常捕获到,并最终得到输出。
4.分组
同一个字段可能在不同情况下有不同校验规则,比如添加和更新两种情况,则可以使用分组。
先创建两个接口,更新:public interface UpdateGroup {},添加:public interface AddGroup {},这两个接口不需要添加任何额外的东西。
接着对对应字段添加分组,比如:
@NotNull(message = "修改必须指定品牌id",groups = {UpdateGroup.class})
@Null(message="新增不能指定id",groups = {AddGroup.class})
private Long brandId;
接着在contronller中,添加分组
@RequestMapping("/save")
public R save(@Validated(AddGroup.class) @RequestBody CategoryEntity category){
categoryService.save(category);
return R.ok();
}
注意,没有添加分组的字段,校验规则会不生效
5.自定义校验
对字段使用自定义注解@ListValue
/**
* 显示状态[0-不显示;1-显示]
*/
@ListValue(vals={0,1},groups = {AddGroup.class, UpdateStatusGroup.class})
@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
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 {
//以下三行为默认添加的内容。
String message() default "{com.atguigu.common.valid.ListValue.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int[] vals() default {};
}
在resource中创建ValidationMessages.properties,并添加以下内容:
com.atguigu.common.valid.ListValue.message=必须提交指定的值
这样就得到了默认的提示信息。
可以看到上面写的自定义注解用了ListValueConstraintValidator.class,则创建相应的自定义校验器:
public class ListValueConstraintValidator implements ConstraintValidator<ListValue,Integer> {
Set<Integer> set = new HashSet<>();
//初始化方法
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for(int val:vals){
set.add(val);
}
}
//判断是否校验成功
/***\
*
* @param integer 需要校验的值
* @param constraintValidatorContext
* @return
*/
@Override
public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
return set.contains(integer);
}
}