JSR303 数据校验

一、前言

首先我们要搞懂为什么要做后台数据校验,在前台做处理不久行了吗?

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>
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值