springboot+validation实现业务校验初尝试

在springboot工程开发中,会对入参进行校验,以保证接口的安全稳定。参数校验通常引入validation校验框架,在实体类中的属性上加上校验注解(@Null、@Blank等),具体使用方式可看大佬相关文章。
在对入参校验之后,通常会有业务规则的校验需求,通常通过 if 批判,然后 throw 异常的方式处理。
典型场景,新增用户时,需要校验其唯一性,用户的唯一性标识不能存在;编辑用户时,需要校验其冲突性,用户的唯一性标识不能与存在的用户重复,只能与自己的重复。
通常在service层处理,代码逻辑如下:

public void add(User user) {
    User result = ***query***;
    if (result != null) {
        throw new ***Exception("用户已存在,请重新输入");
    }
}

那有没有更优雅的方式呢?减少校验对业务代码的入侵?
借鉴参数校验的方式,通过自定义注解完成业务校验。

实例
1、pom

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

2、自定义注解
新增时:表示一个用户是唯一的,唯一性包含:用户名,手机号码、邮箱

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
@Constraint(validatedBy = UserValidator.UniqueUserValidator.class)
public @interface Unique {

    String message() default "用户名、邮箱、手机号码不允许与现存用户重复";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

修改时:表示一个用户的信息是无冲突的,无冲突是指该用户的敏感信息与其他用户不重合

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER, ElementType.TYPE})
@Constraint(validatedBy = UserValidator.NotConflictUserValidator.class)
public @interface NotConflict {

    String message() default "用户名、邮箱、手机号码与现存用户产生重复";

    Class<?>[] groups() default {};

    Class<? extends Payload>[] payload() default {};
}

3、业务规则校验
想让自定义验证注解生效,需要实现 ConstraintValidator 接口。ConstraintValidator 接口的实现类无需添加 @Component 它在启动的时候就已经被加载到容器中了。

public class UserValidator<T extends Annotation> implements ConstraintValidator<T, User> {

    protected Predicate<User> predicate = c -> true;

    @Override
    public boolean isValid(User user, ConstraintValidatorContext constraintValidatorContext) {
        return predicate.test(user);
    }

    /**
     * 校验用户是否唯一
     * 即判断数据库是否存在当前新用户的信息,如用户名,手机,邮箱
     */
    public static class UniqueUserValidator extends UserValidator<Unique> {
        @Override
        public void initialize(Unique unique) {
            predicate = c -> {
                // 持久层查询是否存在
                return false;
            };
        }
    }

    /**
     * 校验是否与其他用户冲突
     * 将用户名、邮件、电话改成与现有完全不重复的,或者只与自己重复的,就不算冲突
     */
    public static class NotConflictUserValidator extends UserValidator<NotConflict> {
        @Override
        public void initialize(NotConflict notConflict) {
            predicate = c -> {
                Collection<User> collection = null;
                // 持久层查询是否冲突
                return false;
            };
        }
    }

}

4、测试

@RestController
@Validated
public class UserController {

    @PostMapping("add")
    public User addUser(@Unique @Valid User user){
        System.out.printf("save user id is %s", user.getId());
        return user;
    }

    @PostMapping("update")
    public User updateUser(@NotConflict @Valid User user){
        System.out.printf("update user is %s", user.getId());
        return user;
    }
}

结果

{
    "status": false,
    "code": 400,
    "message": "addUser.user: 用户名、邮箱、手机号码不允许与现存用户重复",
    "data": null
}

使用方式和入参校验一样,只需要在方法上加上自定义注解即可,service层不需要添加业务校验的代码了。
可见,业务校验和业务逻辑解耦了,在需要校验时用@Validated注解自动触发即可。

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值