Spring Validation 入参校验(自定义)

背景:

1.Java API规范(JSR303)定义了Bean校验的标准validation-api,但没有提供实现。hibernate validation是对这个规范的实现,并增加了校验注解如@Email、@Length等。

2.Spring Validation是对hibernate validation的二次封装,用于支持spring mvc参数自动校验。

3.我们将会在Spring Validation 的基础上在定向的去再次通过自定义注解的方式进行 “分装”成我们的工具类。

实现过程

1.pom.xml 添加 Spring Validation 依赖

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-validation</artifactId>
    <version>2.4.4</version>
</dependency>

2.Controller 层Method 添加 @Validated

@RequestMapping(value = "valid", method = RequestMethod.POST)
public String valid(@RequestBody @Validated Person person) {
    System.out.println(person);
    return "success";
}

3.实体类中增加注解

@Data
public class Person {
​
    @isEmail
    private String email;
​
    @isIdCard
    private String idCard;
​
   // @isContainChineseChar
    private String entEnName;
​
    @NotEmpty //Spring Validation 自带注解
    private String entCNName;
​
    @Valid
    private EntInfo entInfo;
​
    @Valid
    private List<EntInfo> entInfoList;
​
    @IdCard(message = "id错啦,快去检查")
    private Integer id;
​
    @DateType
    private Date createTime;

4.统一异常处理

如果校验失败,会抛出BindException异常,在实际的开发中肯定是要统一处理下异常来返回一个友好的提示。所以对这个异常进行了处理。

@RestControllerAdvice
public class BindExceptionHandler {
    @ExceptionHandler(BindException.class)
    public String handleBindException(HttpServletRequest request, BindException exception) {
        List<FieldError> allErrors = exception.getFieldErrors();
        StringBuilder sb = new StringBuilder();
        for (FieldError errorMessage : allErrors) {
            sb.append(errorMessage.getField()).append(": ").append(errorMessage.getDefaultMessage());
        }
        System.out.println(sb.toString());
        return sb.toString();
    }
}

5.分组校验 (Spring Validation )自带功能

在实际项目中,可能多个方法需要使用同一个DTO类来接收参数,而不同方法的校验规则很可能是不一样的。这个时候,简单地在DTO类的字段上加约束注解无法解决这个问题。因此,spring-validation支持了分组校验的功能,专门用来解决这类问题。

@isContainChineseChar(message = "该字符串中包含中文",groups = Save.class)
private String entEnName;
​
@NotEmpty(message = "内容不能为空",groups = Update.class) //Spring Validation 自带注解
private String entCNName;
​
/**
 * 保存的时候校验分组
 */
public interface Save {
}
​
/**
 * 更新的时候校验分组
 */
public interface Update {
}

@Validated注解上指定校验分组

@PostMapping("/save")
public Result saveUser(@RequestBody @Validated(UserDTO.Save.class) UserDTO userDTO) {
    // 校验通过,才会执行业务逻辑处理
    return Result.ok();
}
​
@PostMapping("/update")
public Result updateUser(@RequestBody @Validated(UserDTO.Update.class) UserDTO userDTO) {
    // 校验通过,才会执行业务逻辑处理
    return Result.ok();
}

6.嵌套校验/集合校验

实际场景中,有可能某个字段也是一个对象,这种情况先,可以使用嵌套校验。如果请求体直接传递了json数组给后台,并希望对数组中的每一项都进行参数校验。如果我们直接使用java.util.Collection下的list或者set来接收数据,参数校验并不会生效!

@Valid
private EntInfo entInfo;
​
@Valid
private List<EntInfo> entInfoList;

添加@Valid注解

以上其实都是 Spring Validation 自己分装的。

我们需要做的就是踩在巨人的肩膀上,做自己要做的事儿

7.自定义注解

业务需求总是比框架提供的这些简单校验要复杂的多,我们可以自定义校验来满足我们的需求。

简单举例身份证验证:

@Documented
@Constraint(validatedBy = isIdCard.NameValidationValidator.class)
@Target({ElementType.METHOD, ElementType.FIELD})
@Retention(RUNTIME)
public @interface isIdCard {
​
    String message() default "身份证内容为空或身份证内容格式错误!";
​
    Class<?>[] groups() default {};
​
    Class<? extends Payload>[] payload() default {};
​
    class NameValidationValidator implements ConstraintValidator<isIdCard, String> {
​
        @Override
        public boolean isValid(String value, ConstraintValidatorContext context) {
            if (isIdCard(value)){
                return true;
            }
            String defaultConstraintMessageTemplate = context.getDefaultConstraintMessageTemplate();
            System.out.println("default message :" + defaultConstraintMessageTemplate);
            //禁用默认提示信息
            //context.disableDefaultConstraintViolation();
            //设置提示语
            //context.buildConstraintViolationWithTemplate("can not contains blank").addConstraintViolation();
            return false;
        }
​
        /** 校验身份证号码 */
        public static boolean isIdCard(String idCard) {
            return StringUtil.isNotEmpty(idCard)
                    && idCard.matches(
                    "[1-9]\\d{5}(18|19|20|(3\\d))\\d{2}((0[1-9])|(1[0-2]))(([0-2][1-9])|10|20|30|31)\\d{3}[0-9Xx]");
        }
    }
​
}

@Target (作用范围:方法,属性,构造方法等),

@Retention(生命范围:源代码,class,runtime)

实现ConstraintValidator接口编写约束校验器

这样我们就可以使用 @isIdCard 注解进行验证了。使用方式和 Spring Validation 中的注解使用是一样的。

8 编程式校验

其实在我们通过注解的方式进行校验外,我们也可以通过编程式调用校验。

 

9.快速失败(Fail Fast)

Spring Validation 默认会校验完所有字段,多个注解都要校验,然后才抛出异常。可以通过一些简单的配置,开启Fali Fast模式,一旦校验失败就立即返回

@Bean
public Validator validator() {
    ValidatorFactory validatorFactory = Validation.byProvider(HibernateValidator.class)
            .configure()
            // 快速失败模式
            .failFast(true)
            .buildValidatorFactory();
    return validatorFactory.getValidator();
}

  • 3
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

XC_Aaron

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值