如何優雅的做參數校驗-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 , UserDto.class);

    // 3:靠代碼進行字段規則判斷
    if(StringUtils.isBlank(dto.getUsername)){
        throw new ServiceException("password不能爲空");
    }
    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>> validate1 = adapter.validate(loginDto);
        Set<ConstraintViolation<LoginDto>> validate2 = validator.validate(loginDto);

        validate1.forEach(v -> logger.error("JSR校驗異常,property:{},message:{}", v.getPropertyPath(), v.getMessage()));
        validate2.forEach(v -> logger.error("JSR校驗異常,property:{},message:{}", v.getPropertyPath(), 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("[ _`~!@#$%^&*()+=|{}':;,\\[\\].<>/?!¥…()—【】‘;:”“’。,、?]|\n|\r|\t").matcher(value).find())
            special = true;

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

使用@Password 註解修飾

    @Password
    private String password;

看效果:
drawing

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章