概述:
项目当中,需要对数据进行数据校验,否则就会传入不符合要求的数据。在客户端做出了校验后服务端也要做出校验,因为用户可以绕过客户端给服务端发送请求。
在实体类中加上JSR303注解,详细JSR303注解可以自行百度搜索,这里只演示校验注解的用法。
message: 说明不符合参数要求显示的信息
groups: 分组校验,用来进行不同场景下的不同校验
因为在项目中,新增不能有id,修改必须有id,此时可以用分组校验来实现此功能
@NotNull(message = "id不能为空",groups = {UpdateGroup.class})
@Null(message = "id必须为空",groups = {AddGroup.class})
private Long id;
@NotBlank(message = "名字不能为空")
private String name;
Controller层:
用@Valid标注需要参数校验的实体类,用BindingResult接口来接收参数格式不正确时出现的返回信息
@RequestMapping("/save")
public R save(@Valid @RequestBody BrandEntity brand,BindingResult bindingResult){
// 创建收集信息的map
Map<String, String> map = new HashMap<>();
// 获取参数错误时的返回信息
bindingResult.getFieldErrors().stream().forEach((fieldError)->{
// getField(): 参数校验失败的字段
// getDefaultMessage() : 参数校验失败的信息
map.put(fieldError.getField(),fieldError.getDefaultMessage());
});
brandService.save(brand);
return R.ok().put("data",map);
}
出现的问题:
@Valid注解无法指定参数在什么场景下需要使用什么样的数据校验,比如新增方法不能有id,修改方法得有id。这个时候需要使用@Validated注解并且指定分组。
并且这样写的话每个映射器都需要写收集信息的代码,这样会造成代码冗余,可以写一个全局异常处理类,来进行捕获处理。
分组校验:
创建对应的分组接口
新增分组
public interface AddGroup {
}
修改分组
public interface UpdateGroup {
}
解决:
Controller层
@Validated(AddGroup.class) 指定是新增分组,和实体类中的分组校验对应该校验才会生效,不对应不会生效。
@RequestMapping("/save")
public R save(@Validated(AddGroup.class) @RequestBody BrandEntity brand){
brandService.save(brand);
return R.ok().;
}
统一处理异常返回接口
@RestControllerAdvice : 统一异常处理接口 basePackages : 指定哪个包下需要统一处理异常
MethodArgumentNotValidException : 该类是参数校验失败具体的异常信息类,可以使用该类来获取数据校验失败后返回的信息。
@RestControllerAdvice(basePackages = "com.xhy.qaq.product.controller")
@Slf4j
public class ExceptionControllerAdvice {
// 统一异常处理
@ExceptionHandler(MethodArgumentNotValidException.class)
public R exception(MethodArgumentNotValidException e){
// e.getBindingResult():获取BindingResult
BindingResult bindingResult = e.getBindingResult();
HashMap<String, String> map = new HashMap<>();
// 收集数据校验失败后的信息
bindingResult.getFieldErrors().stream().forEach((fieldError)->{
map.put(fieldError.getField(),fieldError.getDefaultMessage());
});
log.info(e.getMessage());
// 响应给前端
return R.error().put("data",map);
}
}
自定义注解:
在实际中,JSR303的常用注解肯定是不够用的,这个时候需要自定义注解,就比如某个参数只能为1或者0,在JSR303中没有此注解,需要自定义
@ListValid(values = {0,1},groups = {AddGroup.class})
private Integer Status;
自定义注解步骤:
1.创建数据校验注解,注解必须要有三个属性,以及其他JSR303上面有的注解
String message() default “参数格式不正确”;
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
@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 ListValid {
// 数据校验失败时返回的信息
String message() default "参数格式不正确";
// 分组需要
Class<?>[] groups() default {};
// payload
Class<? extends Payload>[] payload() default {};
// 自定义需要校验的属性
int[] values() default {};
}
2.创建数据校验具体实现类并且实现ConstraintValidator接口
ConstraintValidator<ListValid,Integer> ListValid:自定义注解, Integer : 需要校验的数据类型
public class ListValueConstraintValidator implements ConstraintValidator<ListValid,Integer> {
private Set<Integer> set = new HashSet<>();
/**
* 初始化方法
* @param constraintAnnotation : 自定义注解中的参数
*/
@Override
public void initialize(ListValid constraintAnnotation) {
// 获取自定义数据校验注解中指定符合要求的数据
int[] values = constraintAnnotation.values();
for (int vas:values){
set.add(vas);
}
}
/**
* 校验方法
* @param integer : 需要校验的值
* @param constraintValidatorContext
* @return
*/
@Override
public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
return set.contains(integer);
}
}