Bean 上验证

Bean 上验证

一、Validation 验证

系统开发中,我们通过一系列的规则校验,来确保输入数据的正确性、一致性和安全性。

验证不仅仅是确保正确的用户拥有正确的权限,关注 「你是谁」「你能做什么」,而且要关注「做的对不对」,数据质量的准确性同样可能导致验证的安全问题。

缺失校验会造成缺陷,过度的校验则会使代码显得繁琐。

场景:用户注册

用户: 填写表单数据,提交

系统:throw new RestException(“手机号必填”)

用户:填写手机号

系统:throw new RestException(“邮箱格式不正确”)

用户:修改邮箱格式

系统:throw new BusinessException(“邮箱已经注册过了”)

用户:换一个邮箱

系统:throw new DAOException(“用户名过长,插入失败”)

用户:…

二、验证放哪里合适

对于简单的必填或长度校验,前端需要做,但后端同样需要做一次验证。

验证应该放在 controller 层,还是放在 service 层?

1、在 controller 做,service 不做,理由是如果调用了两个 service 方法 A.method()、B.method() ,两个 service 要做重复校验。

2、在 service 做,controller 不做,理由是简单校验已经由前端拦住,业务相关的校验应该由具体的 service 做,业务相关的东西放到 controller 不合适。

3、controller、service 各做各的,controller 做参数简单验证,service 做业务相关验证,和上述注册的例子是一致的。

4、各层都做,比如在 DAO 层做最终一道拦截,确保不会出现数据问题。

三、Bean 上验证

Java 中有专门的验证标准,Bean Validation 1.0 (JSR 303)、Bean Validation 1.1 (JSR 349)、Bean Validation 2.0 (JSR 380)。

JSR,Java Specification Requests 的缩写,意思是 Java 规范提案。是指向 JCP (Java Community Process) 提出新增一个标准化技术规范的正式请求。JSR 已成为 Java 界的一个重要标准。

Jakarta Bean Validation,2.0 版本发布与 2019 年。

2009 年,Oracle 收购 SUN ,并将开源部分移交给 Eclipse 基金会,但是有商业要求,如不允许再使用 Java EE 等名称,于是基金会改名 Jakarta EE。本质上 Jakarta Bean Validation = Java Bean Validation。

我们可以从分层中抽离出来,针对于 bean 单独做验证。

常规的验证方式可以借助 Bean Validation 提供的一些注解来操作:

@NotBlank
@NotEmpty
@Range
@Length

比如:

@NotBlank(message = "用户账号不能为空")
private String userCode;

四、验证的封装

如果系统接口较多,需要做校验的入参量级比较大,@NotBlank(message = “用户账号不能为空”),这个注解就需要重复 N 多次。

通过进行一层封装可以使之变得更简单。

自定义一个简单的注解

定义一个注解 @UserCodeNotBlank,代表用户系统号不能为空,其验证逻辑在 ParamValidation.UserCodeNotBlankValidate.class 中。

/**
 * @author lyqiang
 */
@Documented
@Retention(RUNTIME)
@Target({FIELD, METHOD, PARAMETER, TYPE})
@Constraint(validatedBy = ParamValidation.UserCodeNotBlankValidate.class)
public @interface UserCodeNotBlank {

    String message() default "系统号不能为空";

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

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

然后定义一个校验规则

/**
 * @author lyqiang
 * <p>
 * 一些常用 (比如账号)
 * 或需要做逻辑判断的校验 (比如 必须是经理)
 * 统一放到此处
 */
public class ParamValidation<T extends Annotation> implements ConstraintValidator<T, Object> {

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

     @Inject
     protected EhrApiAdapter ehrApiAdapter;

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

    public static class UserCodeNotBlankValidate extends ParamValidation<UserCodeNotBlank> {

       @Override
        public void initialize(UserCodeNotBlank constraintAnnotation) {
            predicate = c -> Objects.nonNull(c) && StringUtils.isNotBlank((String) c);
        }
    }
}

使用方的 Bean 上或者 controller 方法参数上直接加注解。

@UserCodeNotBlank
private String userCode;

稍微复杂的注解

我们也可以支持其它一些通用的业务校验操作,比如验证用户角色必须是经理。

public static class GroupChiefCheckValidate extends ParamValidation<GroupChiefCheck> {
    @Override
     public void initialize(GroupChiefCheck groupChiefCheck) {
        predicate = c -> Objects.nonNull(c) && RoleEnum.GROUP_CHIEF ==     ehrApiAdapter.getUserInfo((String) c).getRole();
     }
}

其它场景应用这种方式校验也非常合适,比如:

  • 一些合法性的校验,入参字段必须属于某个枚举中的值,state in [1,2,3,4,5]  等等;
  • 一些操作类的如新增某表记录的接口,需要校验库存当前不存在此记录,否则不允许进行 insert 。


原文始发于微信公众号(郭儿的跋涉):Bean 上验证

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/35470.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!