使用hibernate validate做引數校驗

Coding發表於2020-08-01

1.為什麼使用hibernate validate

​ 在開發http介面的時候,引數校驗是必須有的一個環節,當引數校驗較少的時候,一般是直接按照校驗條件做校驗,校驗不通過,返回錯誤資訊。比如以下校驗使用者名稱不為空的校驗:

if (userName == null || "".equals(userName)) {
    response.setCode(10001);
    response.setMessage("使用者名稱不能為空!");
    return response;
}

但是當介面引數很多,並且引數校驗很負責的時候,如果繼續使用這種校驗的方式,校驗程式碼會非常多,並且難以維護。那麼在這種情況下可以考慮使用hibernate validate做引數校驗。

2.hibernate validate簡介

hibernate validate是基於註解來實現的引數校驗框架,並且有很好的擴充套件性,使用者可以通過自定義約束條件來實現自定義的校驗條件。以下為新增註解的一個小例子:

public class Car {

   @NotNull
   private String manufacturer;

   @NotNull
   @Size(min = 2, max = 14)
   private String licensePlate;

   @Min(2)
   private int seatCount;
}
2.1 springboot專案做基本校驗

​ 新建springboot專案,並且在專案中新增hibernate validate依賴,在springboot2.0版本中的spring-boot-starter-web已經包含了此jar包,不需要再重複新增,但是在spring-boot-starter-web2.0以上版本中不包含此jar包,需要手動新增,依賴資訊如下:

<!-- https://mvnrepository.com/artifact/org.hibernate.validator/hibernate-validator -->
<dependency>
    <groupId>org.hibernate.validator</groupId>
    <artifactId>hibernate-validator</artifactId>
    <version>6.1.5.Final</version>
</dependency>

新增Validator的bean配置,配置內容如下:

@Configuration
public class ValidatorConfiguration {

    @Bean
    public Validator validator(){

        ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class )
                .configure()
                .addProperty( "hibernate.validator.fail_fast", "true" )
                .buildValidatorFactory();
        Validator validator = validatorFactory.getValidator();

        return validator;
    }
}

Validator的實現必須是執行緒安全的,因此可以配置了bean之後,在全專案中使用,並且能多次使用.

建立物件:

public class Company {

    @NotBlank(message = "商品名稱不能為空")
    private String name;

    @Size(min = 2, max = 10, message = "稅號長度必須在2到10位之前")
    private String taxNum;

    @Min(13)
    @Pattern(regexp = "[+-]?[0-9.]+$", message = "手機號碼只能是數字")
    private String phoneNum;

    public String getName() {
        return name;
    }
    ....(相關get和set方法)
}

執行以下測試類:

@SpringBootTest
class HibernateValidateDemoApplicationTests {
    @Autowired
    protected Validator validator;
    @Test
    void contextLoads() {
        Company company = buildCompany();

        Set<ConstraintViolation<Company>> validResultSet = validator.validate(company);
        for (ConstraintViolation<Company> validResult : validResultSet) {
            System.out.println(validResult.getMessage());
        }
    }
    private Company buildCompany() {
        Company company = new Company();
        company.setName("中國石化(浙江石油分公司)");
        company.setTaxNum("123123123123");
        company.setPhoneNum("13333333333");
        return company;
    }
}

輸出結果為:稅號長度必須在2到10位之前

以上例子中的註解比較簡單,通過新增

@NotBlank(message = "商品名稱不能為空")
@Size(min = 2, max = 10, message = "稅號長度必須在2到10位之前")
@Min(13)
@Pattern(regexp = "[+-]?[0-9.]+$", message = "手機號碼只能是數字") 通過正規表示式校驗字元竄

來做一些字串非空、長度的校驗.常用的校驗註解有以下幾種:

註解 校驗規則
AssertFalse、AssertTrue 判斷值是否為false或者true
DecimalMax、DecimalMin 必須為數字,並且值小於最大值、大於最小值
Digits 必須是數字
Email 必須是郵箱
Max、Min、NotBlank、NotEmpty、Size 最大最小長度校驗
Negative、NegativeOrZero 數值校驗
Pattern 正規表示式校驗
2.2 自定義校驗規則

除了上面框架提供的校驗規則, 我們也可以自定義校驗規則,比如當我們要校驗字元個數的時候,可以使用一下自定義規則。

首先定義註解:

@Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE })
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CharacterValidator.class)
@Documented
public @interface CharLength {

    int min() default 0;

    int max() default Integer.MAX_VALUE;

    String message() default "{org.hibernate.validator.constraints.Length.message}";

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

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

定義此註解對應的校驗實現類:

public class CharacterValidator implements ConstraintValidator<CharLength, String> {

    private static final Log log = LoggerFactory.make(MethodHandles.lookup());

    private int min;

    private int max;

    @Override
    public void initialize(CharLength constraintAnnotation) {
        min = constraintAnnotation.min();
        max = constraintAnnotation.max();
        validateParameters();
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext constraintValidatorContext) {
    //為具體的校驗規則
        if ( value == null ) {
            return true;
        }

        int length = CharUtil.getStringLength(value);
        return length >= min && length <= max;
    }

    private void validateParameters() {
        if ( min < 0 ) {
            throw log.getMinCannotBeNegativeException();
        }
        if ( max < 0 ) {
            throw log.getMaxCannotBeNegativeException();
        }
        if ( max < min ) {
            throw log.getLengthCannotBeNegativeException();
        }
    }
}

定義完成之後,對Company的定義修改如下:

@NotBlank(message = "公司名稱不能為空")
@CharLength(max = 12, message = "公司名稱不能超過12個字元")
private String name;

再次執行測試用例,輸出內容如下:公司名稱不能超過12個字元

2.3 使用@ScriptAssert校驗引數

但是當我們的校驗規則更加複雜的時候,只是用註解可能不能完成我們的需求,這個時候就可以使用@ScriptAssert註解來實現執行方法的方式來實現複雜校驗。

在Company類上新增以下註解:

@ScriptAssert(lang = "javascript", script = "com.zjut.hibernate.validate.business.CompanyValidateScript.checkCombineLength(_this.name,_this.taxNum, 30)",
        message = "名稱和稅號不能超過30位")

並定義校驗方法:

public static boolean checkCombineLength(int maxLength, String... params) {
    int length = 0;
    for (String param : params) {
        if (StringUtils.isEmpty(param)) {
            continue;
        }
        length += CharUtil.getStringLength(param);
    }
    return length <= maxLength;
}

除此之外,hibernater validate還支援分組校驗、校驗集合等功能,具體可參考官方文件:

http://hibernate.org/validator/

相關文章