一、前言
首先我们要搞懂为什么要做后台数据校验,在前台做处理不久行了吗?
1-1后台校验与前台校验区别:
发生位置不同: 后台校验是在服务器端发生通过代码判断,而前端校验发生在浏览器主要通过JavaScript校验返回错误信息。
对比:
- 后台数据校验更加安全和可靠,可以确保接收到的数据符合预期并遵循业务规则和逻辑。
- 而前端校验可以提高用户体验,避免不必要的网络请求和后台验证。
因此,这两种数据校验方式都有其独特的优点,应根据业务需求及其相关安全性要求进行选择和操作。
二、简介
JSR-303是Java平台的Bean Validation规范,旨在为Java应用程序提供基于注释的数据验证。其全称为Java Specification Request 303。
下面按照JSR-303定义的常见注解名称进行列出,并解释了每个注解表示的数据校验约束。
1. @NotNull:校验属性不为null,可以用于校验任意类型的属性。
2. @NotEmpty:校验属性不能为空,可以用于验证字符串、数组、集合等类型的属性。
3. @NotBlank:校验字符串属性不为空白,即不为null、不为空、不包含只有空白字符的字符串。
4. @Size:校验字符串、集合或数组类型属性的大小。指定验证范围,包含最小值、最大值和允许值。
5. @Max和@Min:校验数字属性的最大值和最小值。可以用于校验各种数字类型,如int、long、double等。
6. @Email:校验属性是否符合电子邮件地址的格式。
7. @Pattern:校验属性是否满足指定的正则表达式。
8. @AssertTrue:校验boolean类型属性是否为true
9. @AssertFalse: 校验boolean类型属性是否为false
10. @Future和@FutureOrPresent:对日期类型的属性进行校验,确保它们表示的日期在给定时间之后。
11. @Past和@PastOrPresent:和上面相反,确保日期表示的时间早于或等于给定时间。
12. @Valid:嵌套约束,用于指示引用一个嵌套对象,然后对嵌套对象进行校验。
13. @DecimalMax和@DecimalMin:校验数字属性的最大值和最小值,可以用于校验BigDecimal和BigInteger类型,并且支持指定整数位和小数位的数量。
总之,JSR-303提供了一些常用的注解,可以用于指定数据验证的约束和规则。通过这些注解,可以很方便地在Java Bean中实施数据校验。
三、导入依赖
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> </dependency>
四、@Validated、@Valid区别
@Validated:
- Spring提供的
- 支持分组校验
- 可以用在类型、方法和方法参数上。但是不能用在成员属性(字段)上
- 由于无法加在成员属性(字段)上,所以无法单独完成级联校验,需要配合@Valid
@Valid:
- JDK提供的(标准JSR-303规范)
- 不支持分组校验
- 可以用在方法、构造函数、方法参数和成员属性(字段)上
- 可以加在成员属性(字段)上,能够独自完成级联校验
@Validated用到分组时使用,一个学校对象里还有很多个学生对象需要使用@Validated在Controller方法参数前加上,@Valid加在学校中的学生属性上,不加则无法对学生对象里的属性进行校验!
示列如下:
@Data
public class School{
@NotBlank
private String id;
private String name;
@Valid // 需要加上,否则不会验证student类中的校验注解
@NotNull // 且需要触发该字段的验证才会进行嵌套验证。
private List<Student> list;
}
@Data
public class Student {
@NotBlank
private String id;
private String name;
private int age;
}
@PostMapping("/test")
public Result test(@Validated @RequestBody School school){
}
五、定义全局异常,统一返回类型
/**
* 集中处理异常
*
* @author syf
* @version 1.0
* @see
* @since
*/
@Slf4j
//@ResponseBody
//@ControllerAdvice(basePackages = "com.atguigu.gulimall.product")
//统一异常处理
@RestControllerAdvice(basePackages = "com.atguigu.gulimall.product")
public class GulimallExceptionControllerAdvice {
//ExceptionHandler 精确匹配 MethodArgumentNotValidException 异常
@ExceptionHandler(value = MethodArgumentNotValidException.class)
public R handleVaildException(MethodArgumentNotValidException e){
log.error("处理数据校验异常", e.getMessage(), e.getClass(), e);
HashMap<Object, Object> map = new HashMap<>();
e.getBindingResult().getFieldErrors().stream().forEach(en->{
//属性名称
String field = en.getField();
//错误信息
String defaultMessage = en.getDefaultMessage();
map.put(field, defaultMessage);
});
return R.error(BizCodeMeum.VAILD_EXCEPTION.getCode(), BizCodeMeum.VAILD_EXCEPTION.getMsg()).put("data", map);
}
@ExceptionHandler(value = Throwable.class)
public R handleException(Throwable throwable){
log.error("处理全部异常", throwable.getMessage(), throwable.getClass(), throwable);
return R.error(BizCodeMeum.VAILD_EXCEPTION.getCode(), BizCodeMeum.VAILD_EXCEPTION.getMsg());
}
}
枚举
public enum BizCodeMeum {
UNKNOW_EXCEPTION(10000, "系统未知异常"),
VAILD_EXCEPTION(10001, "参数格式校验失败");
private int code;
private String msg;
BizCodeMeum(int code, String msg){
this.code = code;
this.msg = msg;
}
public int getCode() {
return code;
}
public String getMsg() {
return msg;
}
}
六、分组校验
区分不同场景比如,只有实体类,新增和更新实际校验的需要字段是不一样的。
创建不同的接口,用于区分新增和更新
//AddGroup 接口
public interface AddGroup {
}
//UpdateGroup
public interface UpdateGroup {
}
实体类注解如下
group 指定使用的新增、修改组别
/**
* 品牌
*
* @author shiyefei
* @email 1336182731@qq.com
* @date 2021-12-20 15:22:10
*/
@Data
@TableName("pms_brand")
public class BrandEntity implements Serializable {
private static final long serialVersionUID = 1L;
/**
* 品牌id
*/
@Null(message = "新增不能指定id", groups = {AddGroup.class})
@NotNull(message = "修改必须指定id", groups = {UpdateGroup.class})
@TableId
private Long brandId;
/**
* 品牌名
*/
@NotBlank(message = "品牌名必须提交", groups = {AddGroup.class, UpdateGroup.class})
private String name;
/**
* 品牌logo地址
*/
@URL(message = "logo地址必须是个合法的地址", groups = {AddGroup.class, UpdateGroup.class})
@NotEmpty
private String logo;
/**
* 介绍
*/
@NotEmpty(groups = {AddGroup.class, UpdateGroup.class})
private String descript;
/**
* 显示状态[0-不显示;1-显示]
*/
@ListValue(valus={0,1} )
@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
private Integer showStatus;
/**
* 检索首字母
*/
@NotEmpty(groups = {AddGroup.class, UpdateGroup.class})
@Pattern(regexp = "^[a-zA-Z]$" , message = "检索首字母必须是一个字母")
private String firstLetter;
/**
* 排序
*/
@NotNull(groups = {AddGroup.class, UpdateGroup.class})
@Min(value = 0, message = "排序最小为0")
private Integer sort;
}
controller如下:
@Validated
/**
* 新增
*/
@RequestMapping("/save")
public R save(@Validated({AddGroup.class}) @RequestBody BrandEntity brand){
}
/**
* 修改
*/
@RequestMapping("/update")
//@RequiresPermissions("product:brand:update")
public R update(@Validated({UpdateGroup.class}) @RequestBody BrandEntity brand){
}
七、自定义注解
7-1 自定义注解
/**
* 自定义校验器
* @author yefei.shi
* @return
*/
@Documented
@Constraint(
validatedBy = {LisyValueConstraintValidator.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.vaild.ListValue.message}";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
int[] valus() default {};
}
7-2编写校验器
public class LisyValueConstraintValidator implements ConstraintValidator<ListValue, Integer> {
private Set<Integer> set = new HashSet<>();
//初始化
@Override
public void initialize(ListValue constraintAnnotation) {
int[] valus = constraintAnnotation.valus();
for (int bal: valus) {
set.add(bal);
}
}
//判斷是否校驗成功
/**
* @author yefei.shi
* @param integer: 需要校驗的值
* @param constraintValidatorContext:
* @return boolean
*/
@Override
public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
return set.contains(integer);
}
}
7-3实体类上标注
/**
* 显示状态[0-不显示;1-显示]
*/
@ListValue(valus={0,1},groups = {AddGroup.class, UpdateStatusGroup.class} )
@NotNull(groups = {AddGroup.class, UpdateStatusGroup.class})
private Integer showStatus;
结果演示
8- 坑点
spring版本过高可以尝试下面
<dependency> <groupId>org.hibernate.validator</groupId> <artifactId>hibernate-validator</artifactId> <version>6.0.18.Final</version> </dependency>