Spring Boot 使用 JSR303(@Validated) 实现参数校验

一:简介

1. JSR-303 是 JAVA EE 6 中的一项子规范,叫做 Bean Validation

2. 在任何时候,当你要处理一个应用程序的业务逻辑,数据校验是你必须要考虑和面对的事情。

3. Bean Validation 为 JavaBean 验证定义了相应的元数据模型和 API。

4. constraint 可以附加到字段getter 方法或者接口上面。

5. 对于一些特定的需求,用户可以很容易的开发定制化的 constraint。

6. Bean Validation 是一个运行时的数据验证框架,在验证之后验证的错误信息会被马上返回

二:Bean Validation 规范内嵌的约束注解

限制说明
@Null限制只能为null
@NotNull限制必须不为null
@AssertFalse限制必须为false
@AssertTrue限制必须为true
@DecimalMax(value)限制必须为一个不大于指定值的数字
@DecimalMin(value)限制必须为一个不小于指定值的数字
@Digits(integer,fraction)限制必须为一个小数,且整数部分的位数不能超过integer,小数部分的位数不能超过fraction
@Future限制必须是一个将来的日期
@Max(value)限制必须为一个不大于指定值的数字
@Min(value)限制必须为一个不小于指定值的数字
@Past限制必须是一个过去的日期
@Pattern(value)限制必须符合指定的正则表达式
@Size(max,min)限制字符长度必须在min到max之间
@Past验证注解的元素值(日期类型)比当前时间早
@NotEmpty验证注解的元素值不为null且不为空(字符串长度不为0、集合大小不为0)
@NotBlank验证注解的元素值不为空(不为null、去除首位空格后长度为0),不同于@NotEmpty,@NotBlank只应用于字符串且在比较时会去除字符串的空格
@Email验证注解的元素值是Email,也可以通过正则表达式和flag指定自定义的email格式

三:基本应用

1、引入依赖

<!--引入validation的场景启动器-->
<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
</dependency>

2、给参数对象添加校验注解

private Integer id;
@NotBlank(message = "名称不能为空")
private String name;
private Date data;
@NotNull(message = "年龄不能为空")
private Integer age;
@Pattern(regexp = "[0-9]" ,  message = "参数格式错误")
private String Telephone;

Controller 中需要校验的参数Bean前添加 @Validated开启校验功能,紧跟在校验的Bean后添加一个BindingResult,BindingResult封装了前面Bean的校验结果。

@GetMapping("/into")
public String into(@Validated Emp emp ,BindingResult bindingResult) {
    if(bindingResult.hasErrors()){
        Map<String,String> map = new HashMap<>();
        bindingResult.getFieldErrors().forEach((item) ->{
            String msg = item.getDefaultMessage();
            String fie = item.getField();
            map.put(fie,msg);
        });
        return map.toString();
    }
    return "200";
}

四:异常的统一处理

参数校验不通过时,会抛出 BingBindException 异常,可以在统一异常处理中,做统一处理,这样就不用在每个需要参数校验的地方都用 BindingResult 获取校验结果了。

/**
 * 自定义验证异常(参数传值)
 */
@ExceptionHandler(BindException.class)
public String validatedBindException(BindException e) {
    String message = e.getAllErrors().get(0).getDefaultMessage();
    return message;
}
​
/**
 * 自定义验证异常(json传值)
 */
@ExceptionHandler(MethodArgumentNotValidException.class)
public String MethodArgumentNotValidException(MethodArgumentNotValidException e) {
    String message = e.getBindingResult().getAllErrors().get(0).getDefaultMessage();
    return message;
}

五:分组解决校验

新增和修改对于实体的校验规则是不同的,例如id是自增的时,新增时id要为空,修改则必须不为空;新增和修改,若用的恰好又是同一种实体,那就需要用到分组校验;

 

校验注解都有一个groups属性,可以将校验注解分组,我们看下@NotNull的源码

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
@Retention(RetentionPolicy.RUNTIME)
@Repeatable(NotNull.List.class)
@Documented
@Constraint( validatedBy = {} )
public @interface NotNull {
    String message() default "{javax.validation.constraints.NotNull.message}";
    Class<?>[] groups() default {};
    Class<? extends Payload>[] payload() default {};
    @Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE, ElementType.CONSTRUCTOR, ElementType.PARAMETER, ElementType.TYPE_USE})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface List {
        NotNull[] value();
    }
}

从源码可以看出 groups 是一个Class<?>类型的数组,那么就可以创建一个Groups.

public class Groups {
    public interface Add {
    }
    public interface Update {
    }
}

给参数对象的校验注解添加分组

private Integer id;
@NotBlank(message = "名称不能为空" ,groups = Groups.Add.class)
private String name;
private Date data;
@NotNull(message = "年龄不能为空",groups = {Groups.Add.class,Groups.Update.class})
private Integer age;
@Pattern(regexp = "[0-9]" ,  message = "电话格式错误")
private String Telephone;

在Controller 添加分组;多个可用@Validated({Groups.Update.class,Groups.Add.class})

@GetMapping("/into")
public String into(@Validated({Groups.Update.class})  Emp emp ) {
    return "200";
}

六:配置文件配置massage错误信息

在resource 目录下新建提示信息配置文件“ValidationMessages.properties“

注意:名字必须为“ValidationMessages.properties“ 因为SpringBoot自动读取classpath中的ValidationMessages.properties里的错误信息

@NotBlank(message = "{emp.name}" ,groups = Groups.Add.class)
private String name;
emp.name=参数错误

 

七:自定义校验注解

虽然JSR303和springboot-validator 已经提供了很多校验注解,但是当面对复杂参数校验时,还是不能满足我们的要求,这时候我们就需要 自定义校验注解

例如年龄,用 1代表男 2代表女,我们自定义一个校验注解@ListValue,指定取值只能1和2。

1、创建约束规则

@Target({ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint( validatedBy = {ListValueConstraintValidator.class} )
public @interface ListValue {
    String message() default "{javax.validation.constraints.ListValue.message}";
​
    Class<?>[] groups() default {};
​
    Class<? extends Payload>[] payload() default {};
​
    int[] vals() default {};
}

一个标注(annotation) 是通过@interface关键字来定义的. 这个标注中的属性是声明成类似方法的样式的. 根据Bean Validation API 规范的要求:

  • message属性, 这个属性被用来定义默认得消息模版, 当这个约束条件被验证失败的时候,通过此属性来输出错误信息。

  • groups 属性, 用于指定这个约束条件属于哪(些)个校验组. 这个的默认值必须是Class<?>类型数组。

  • payload 属性, Bean Validation API 的使用者可以通过此属性来给约束条件指定严重级别. 这个属性并不被API自身所使用

除了这三个强制性要求的属性(message, groups 和 payload) 之外, 我们还添加了一个属性用来指定所要求的值. 此属性的名称vals在annotation的定义中比较特殊, 如果只有这个属性被赋值了的话, 那么, 在使用此annotation到时候可以忽略此属性名称.另外, 我们还给这个annotation标注了一些元标注( metaannotatioins):

  • @Target({ METHOD, FIELD, ANNOTATION_TYPE }): 表示此注解可以被用在方法, 字段或者annotation声明上。

  • @Retention(RUNTIME): 表示这个标注信息是在运行期通过反射被读取的.

  • @Constraint(validatedBy = ListValueConstraintValidator.class): 指明使用哪个校验器(类) 去校验使用了此标注的元素.

  • @Documented: 表示在对使用了该注解的类进行javadoc操作到时候, 这个标注会被添加到javadoc当中.

2、创建约束校验器

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);
        }
    }
​
    /**
     * 判断是发否校验成功
     * integer:需要校验的值
     */
    @Override
    public boolean isValid(Integer integer, ConstraintValidatorContext constraintValidatorContext) {
        return set.contains(integer);
    }
}

ListValueConstraintValidator定义了两个泛型参数, 第一个是这个校验器所服务到标注类型(在我们的例子中即ListValue), 第二个这个校验器所支持到被校验元素的类型 (即Integer)。

如果一个约束标注支持多种类型的被校验元素的话, 那么需要为每个所支持的类型定义一个ConstraintValidator,并且注册到约束标注中。

这个验证器的实现就很平常了, initialize() 方法传进来一个所要验证的标注类型的实例, 在本例中, 我们通过此实例来获取其vals属性的值,并将其保存为Set集合中供下一步使用。

isValid()是实现真正的校验逻辑的地方, 判断一个给定的int对于@ListValue这个约束条件来说是否是合法的。

 

在参数对象中使用@ListValue注解。

@ListValue(message = "cs", vals={1,2})
private Integer gender;

谢谢!!!

  • 6
    点赞
  • 22
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
@Valid和@Validated注解在Spring Boot中用于验证输入数据,但它们有一些区别。 @Valid注解是JSR-303规范中定义的注解,用于验证Java Bean中的属性。在Spring Boot中,@Valid注解可以用于验证请求体中的数据。如果请求体中的数据是一个Java Bean对象,可以使用@Valid注解来验证该对象的属性值是否符合要求。\[1\] @Validated注解是Spring框架中的一个注解,它是JSR-303规范的扩展。@Validated注解可以用于在方法级别上校验方法参数。它可以用于验证请求参数和请求体中的数据。@Validated注解可以用于方法级别和类级别上,用于验证方法参数和类属性。\[4\] 在类级别上使用@Validated注解,需要在类上添加该注解。这样可以对整个类的方法参数进行验证。\[3\] 在方法级别上使用@Validated注解,需要在方法参数上添加该注解。这样可以对特定方法的参数进行验证。\[5\] 总结来说,@Valid注解用于验证Java Bean中的属性,而@Validated注解用于在方法级别上校验方法参数。@Validated注解是@Valid注解的扩展,可以用于验证请求参数和请求体中的数据。\[2\]\[4\] #### 引用[.reference_title] - *1* *2* *3* *4* *5* [Spring Boot中的@Validated和@Valid](https://blog.csdn.net/jam_yin/article/details/131207998)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值