数据校验
数据校验一般分为前端校验,后端校验,绕过前端校验直接通过接口直接向后端发送请求,因此可以使用JSR303来实现后端的数据校验,只需要用简单的注解方式,即可完成数据校验,系统给定的校验注解如下,也可以指定自定义校验
@NotBlank:带注释的元素不能为null,并且必须至少包含一个,只能作用在接收的String类型上,长度必须大于0
@NotNull:带注解的元素不能为null,接收任意类型,可以为""
@NotEmpty:带注解的元素不能为null或者其他空值,而且长度必须大于0,一般用在集合类上面
基本校验
在需要校验的属性上添加注解
@NotBlank(message = "名字不能为空")
private String name;
@URL(message = "logo必须合法")
private String logo;
@Pattern(regexp = "^[a-zA-Z]$", message = "检索首字母必须为一个字母")
private String firstLetter;
@Min(value = 0, message = "排序必须大于0")
private Integer sort;
在controller上开启校验功能@Valid
@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);
});
return R.error(400, "提交的数据不合法").put("data", map);
}
brandService.save(brand);
return R.ok();
}
测试数据校验结果
分组校验
声明接口分组类,无意义,仅作声明接口
// 添加分组
public interface AddGroup {}
// 修改分组
public interface UpdateGroup {}
// 查询分组
public interface QueryGroup {}
给校验的注解注明什么分组下需要进行校验
@NotBlank(message = "名字不能为空",groups = {AddGroup.class,UpdateGroup.class})
private String name;
@URL(message = "logo必须合法",groups = {AddGroup.class,UpdateGroup.class})
private String logo;
@Min(value = 0, message = "排序必须大于0",groups = {AddGroup.class})
private Integer sort;
在controller上的校验功能@Valid标明哪些分组使用注解
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){...}
注:如果开启了分组校验后,@Validated没有指定分组,数据校验不生效
校验器全局异常处理
在Controller的方法上声明参数BindingResult result
可以捕获校验异常,但是如果每个controller都有异常需要捕获,代码十分冗余,因此可以定义全局异常校验处理类
定义错误码和错误信息,方便日后维护
public enum BizCodeEnume {
UNKNOW_EXCEPTION(10000,"系统未知异常"),
VAILD_EXCEPTION(10001,"参数格式校验失败");
private int code;
private String msg;
BizCodeEnume(int code,String msg){
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
全局异常处理
@Slf4j
@RestControllerAdvice
public class GlobalExceptionAdvice {
// 捕获校验器异常
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleVaildException(MethodArgumentNotValidException e) {
BindingResult result = e.getBindingResult();
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(BizCodeEnume.VAILD_EXCEPTION.getCode(), BizCodeEnume.VAILD_EXCEPTION.getMsg()).put("data", map);
}
// 捕获其他异常
@ExceptionHandler(value = Exception.class)
public R handleException(Exception e) {
return R.error(BizCodeEnume.UNKNOW_EXCEPTION.getCode(), BizCodeEnume.UNKNOW_EXCEPTION.getMsg());
}
}
测试数据校验结果
自定义校验器
引入依赖
<dependency>
<groupId>javax.validation</groupId>
<artifactId>validation-api</artifactId>
<version>2.0.1.Final</version>
</dependency>
编写自定义校验注解
模仿系统提供的注解,生成自定义注解
@Constraint:指定校验器
@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.common.valid.ListValue.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
// 定义注解方法
int[] vals() default {};
}
编写自定义消息模板
ValidationMessages.properties
com.common.valid.ListValue.message=必须为指定属性
编写自定义校验器
public class ListValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
// 存放注解的值
private Set<Integer> set = new HashSet<>();
// 初始化方法
@Override
public void initialize(ListValue constraintAnnotation) {
int[] vals = constraintAnnotation.vals();
for (int val : vals) {
set.add(val);
}
}
// 判断是否校验成功 value为需要校验的值
@Override
public boolean isValid(Integer value, ConstraintValidatorContext context) {
// 如果校验的值包含在注解里校验成功
return set.contains(value);
}
}
使用自定义注解
// 显示状态[0-不显示;1-显示] 不为0 1就报错
@ListValue(vals = {0, 1})
private Integer showStatus;
测试自定义注解