如何优雅的做参数校验-JSR303规范

摘要:
接口收到加密参数后,第一步应该是对它们进行解密,然后将它们转换为实际参数。编写代码来判断参数字段是否满足条件不够优雅。在参数解密并转换为LoginTo后,这些注释如何发挥其原始作用?编写一个您自己的工具类@Slf4jpublicclassJSRValidatorUtil{privatefinancialstaticValidatorVALIDATOR=Validation.byProvider.config().buildValidatorFactory().getValidator();publicstatic<T>voidvalidate{Set<ConstraintVibration<T>>validate=VALIDATOR.validate;validate.forEach;}}检查测试结果:@TestpublicvoidvalidateTest(){LoginTologinTo=newLoginTo();LoginTo.setUsername;loginTo.setPassword;JSRValidatorUtil.validate;}利用Spring的内置验证器SpringValidatorAdapter@ResourceprivateSpringValidatorAdapteradapter ;@ 资源私有验证器;@Testpublicvoidvalidate(){LoginTologinTo=newLoginTo();LoginTo.setUsername;loginTo.setPassword;Set<ConstraintVibration<LoginTo˃˃validateResult=adapter.validate;//Set<Constraint Vibration>LoginTo˃˃validateResult=validate.validate;validateResult.forEach;}定制符合JSR330规范的注释需要在许多场景中实现用户定义的注释,例如性别、密码、身份证等。
前言:

本文不是讲@Validate、@Valid是如何使用的、区别是什么,想看这些内容的请换篇文章。

背景:

当前端传过来的参数是进行对称性加密、base64加密等处理后过的参数时,在controller接口使用@Validae、@Valid 注解是没用的。
接口收到加密参数后,第一步应该是解密,然后再转换为实际的参数。那么判断参数字段是否符合条件又要写代码去判断了,不够优雅。
那么我是怎么做的呢?看下文...

假设我们现在有一个登录接口,大概是下面这样↓↓↓↓↓↓↓↓↓↓↓

原本的登录逻辑
@PostMapping(value = "/login")
public R login(@RequestParam(value = "param") String param){

    // 1:将参数解密出来拿到解密后的字符串(对称性解密)
    String realParam = rsa.decryptStr(encryptParamsVO.getParams(), KeyType.PrivateKey, StandardCharsets.UTF_8);

    // 2:将解密后的字符串转换为dto
    LoginDto dto = JSON.parseObject(realParam , LoginDto.class);

    // 3:靠代码进行字段规则判断
    if(StringUtils.isBlank(dto.getUsername)){
        throw new ServiceException("username不能为空");
    }
    if(StringUtils.isBlank(dto.getPassword)){
        throw new ServiceException("password不能为空");
    }

    // 4:校验通过后进行登录流程
    userService.login(dto);

    return R.success();
}

可以看到第三步有这明显的缺陷,如果 LoginDto 参数一多,那么就要写很多 if 语句代码来进行判断,这样势必是不优雅的写法。

参数类
@Data
public class LoginDto {

    @NotBlank(message = "用户名不能为空")
    @Size(min = 6, max = 16, message = "用户名格式不正确")
    private String username;

    @NotBlank(message = "密码不能为空")
    @Size(min = 8, max = 16, message = "密码格式不正确")
    private String password;
}

这个类其实才是接口中实际用到的参数类。而它本身的字段其实是用了@NotNull、@Size注解修饰过的,只不过没有起到作用。
那么参数在解密后转换为 LoginDto 后如何让这些注解起到原本的作用呢?

自己写个工具类
@Slf4j
public class JSRValidatorUtil {

    private final static Validator VALIDATOR = Validation.byProvider(HibernateValidator.class)
            .configure()
            .buildValidatorFactory()
            .getValidator();

    public static <T> void validate(T param) {
        Set<ConstraintViolation<T>> validate = VALIDATOR.validate(param);
        validate.forEach(v -> {
            log.error("JSR校验异常,property:{},message:{}", v.getPropertyPath(), v.getMessage());
            throw new ServiceException(v.getMessage());
        });
    }
}

看看测试效果:

    @Test
    public void validateTest() {
        LoginDto loginDto = new LoginDto();
        loginDto.setUsername("123456");
        loginDto.setPassword("123456");

        JSRValidatorUtil.validate(loginDto);
    }
drawing利用Spring自带的Validator、SpringValidatorAdapter
    @Resource
    private SpringValidatorAdapter adapter;
    @Resource
    private Validator validator;

    @Test
    public void validate() {
        LoginDto loginDto = new LoginDto();
        loginDto.setUsername("123456");
        loginDto.setPassword("123456");

        Set<ConstraintViolation<LoginDto>> validateResult = adapter.validate(loginDto);
        //Set<ConstraintViolation<LoginDto>> validateResult = validator.validate(loginDto);

        validateResult.forEach(v -> {
            logger.error("JSR校验异常,property:{},message:{}", v.getPropertyPath(), v.getMessage());
            throw new ServiceException(v.getMessage());
        });
    }

drawing自定义符合JSR330规范的注解

很多场景下需要自定义注解实现的,比如字段属性为 性别、密码、身份证 等等。

自定义一个密码格式校验注解:

@Constraint(validatedBy = PasswordValidation.class)
@Target({ElementType.FIELD, ElementType.METHOD})
@Retention(RetentionPolicy.RUNTIME)
public @interface Password {

    String message() default "密码必须包含大小写英文字符、数字、特殊字符";

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

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

}

规则实现类:

public class PasswordValidation implements ConstraintValidator<Password, String> {

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        boolean haveDigit = false;
        boolean uppercase = false;
        boolean lowercase = false;
        boolean special = false;

        char v;
        for (int i = 0; i < value.length(); i++) {
            v = value.charAt(i);
            if (Character.isDigit(v))
                haveDigit = true;
            else if (Character.isUpperCase(v))
                uppercase = true;
            else if (Character.isLowerCase(v))
                lowercase = true;
        }
        if (Pattern.compile("[ _`~!@#$%^&*()+=|{}':;,\[\].<>/?!¥…()—【】‘;:”“’。,、?]|
|
|	").matcher(value).find())
            special = true;

        return haveDigit && uppercase && lowercase && special;
    }
}

使用@Password 注解修饰

    @Password
    private String password;

看效果:
drawing

免责声明:文章转载自《如何优雅的做参数校验-JSR303规范》仅用于学习参考。如对内容有疑问,请及时联系本站处理。

上篇python+flask拉普拉斯平滑(Laplace Smoothing)下篇

宿迁高防,2C2G15M,22元/月;香港BGP,2C5G5M,25元/月 雨云优惠码:MjYwNzM=

随便看看

小程序实现单选多选功能

applet的单选组件和复选框组件的样式只提供了变化的颜色,这显然不足以满足实际的项目需求,因此您可以自己模拟。脚注:小程序不支持dom1的操作。多个框的模拟实现:实现思路:想法非常简单。使用选中的属性绑定每个选项。类型为布尔型。单击以反转!...

【转】Kettle集群

本文转自:http://blog.csdn.net/dqswuyundong/article/details/5952009KettleCluster Kettle是一个开源ETL工具,以其效率和可扩展性而闻名于业界。其高效的一个重要原因是其多线程和集群功能。Kettle的多线程采用了管道并发机制,这在另一篇文章中专门介绍。本文主要介绍水壶集群。群集允许转换...

windows下mstsc 远程Ubuntu 教程

为远程桌面控制设置Ubuntu 16.04的缺点是重新启动系统需要使用监视器登录系统。首先,我们将Ubuntu远程控制设置为允许远程连接,进入系统-˃首选项-˃桌面共享,或直接搜索桌面共享。如图所示,选中此项,然后选中安全项,并设置远程密码。...

sqlite3 数据类型 批量插入

SQLite3采用动态数据类型。存储值的数据类型与值本身相关,而不是由其字段类型决定。SQLite3的动态数据类型可以向后兼容其他数据库常用的静态类型,这意味着在使用静态数据类型的数据库中使用的数据表也可以在SQLite3中使用。在SQLite2数据库中,除了声明为主键的INTEGER列外,任何列都可以存储属于任何存储类型的值。...

Maven settings.xml配置详解

让我们来谈谈设置。对于Maven,xml相当于全局配置,用于所有项目。maven2-xml中有两个设置,作为全局配置位于maven2的安装目录conf下。对于团队设置,一致的定义是关键,因此maven2/conf Xml下面的设置是团队的通用配置文件。当然,每个成员都需要特殊的用户定义设置,例如用户信息,其他设置也是如此。xml用作本地配置。默认位置为:${...

十四、ES开启密码认证

所以我们需要为es head和kibana添加密码认证。4、 为kibana设置密码。1.为kibana配置证书。因为kibana和es之间的连接也需要证书加密通信。mkdir-p/etc/kibana/certscp/etc/selastic search/certs-*/etc/kibana/certs/2.授予kibana主要权限。权限必须为kiban...