Spring Validation-用註解代替程式碼引數校驗

iceWang丶發表於2020-09-02

Spring Validation

概念

在原先的編碼中,我們如果要驗證前端傳遞的引數,一般是在接受到傳遞過來的引數後,手動在程式碼中做 if-else 判斷,這種編碼方式會帶來大量冗餘程式碼,十分的不優雅。


因此,推出了用註解的方式,來代替手動判斷的方式,讓編碼更加的簡潔。

使用方式

引入註解:
一般在

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

依賴中會有相關依賴,如果沒有的話,可以手動引入下面的依賴。

    <dependency>
      <groupId>org.hibernate.validator</groupId>
      <artifactId>hibernate-validator</artifactId>
      <version>6.0.18.Final</version>
      <scope>compile</scope>
    </dependency>

get

針對 get 方式的請求,我們的請求引數直接放在引數列表裡,因此直接在引數列表里加上想要驗證的註解即可。

  @GetMapping("xss")
    public void xssGet(@RequestParam("xss1") @Size(min = 1,max = 15,message = "長度不對") String xss, HttpServletRequest request) {
      
    }

在方法中,需要驗證 xss 引數是否符合長度規範,即長度在 1-15 個字元之間,如果不對,則會提示出資訊:長度不對。

post

在 post 方法中,傳遞的引數一般比較多,因此大多數情況下,採用的是傳遞的實體類的形式,然後用 json 的形式來傳遞資料,在這種情況下,使用的方式為構建一個實體類,然後在實體類的屬性上新增註解來做。

@Data
public class SaveEmployeeParam implements Serializable {
    private static final long serialVersionUID = 8176094330224588795L;

    @NotEmpty
    private String Id;

    @Size(max = 15, message = "名稱必填,且最多為15個漢字")
    private String nickname;

    @PhoneValidationAnnotation
    private String phone;
}


    @PostMapping
    public ResultEntity saveEmployee( @Valid @RequestBody SaveEmployeeParam saveEmployeeParam) {
        employeeService.saveEmployee(saveEmployeeParam);
        return ResultEntity.success();
    }

通過該方式,在驗證 SaveEmployeeParam 時,框架就會自動在接受引數時,驗證實體類中的值是否符合註解定義的規範。


在這裡,就會驗證 id 不能為空,nickname 的長度最多15個字元,以及我在手機號上新增了一個自定義註解,在確保其符合手機號規範。

分組校驗

有時,我們的一個實體類可能會在多種情況下使用,而又不想每種情況都定義一個實體類,則可以採用分組校驗的方式,在不同的情況下,採用不同的校驗方案。


首先自定義幾種不同情況下的介面:

public interface Create {
}

public interface Update {
}


然後在指定的 pojo 上指定不同的情況下的策略:

@Data
public class Demo {
    @Size(max = 15, groups = Create.class)
    @Size(max = 10, groups = Update.class)
    private String name;
    
    @Max(value = 100, groups = Create.class)
    @Max(value = 20, groups = Update.class)
    private Integer age;
}


最後,在不同的方法上,根絕業務需要指定使用不同的策略即可:

    @PostMapping("xss3")
    public String xssPost(@Validated({Create.class}) @RequestBody Demo xss3) {
        return JSON.toJSONString(xss3);
    }

    @PutMapping("xss4")
    public String xssUpdate(@Validated({Update.class}) @RequestBody Demo xss4) {
       return JSON.toJSONString(xss4);
    }

在這種情況下,則在執行 xssPost() 方法時,採用是 Create 的執行方案,在執行 xssUpdate() 方法時,採用的是 Update 方案。

提供的全部註解

JSR提供的校驗註解:
@Null 必須為 null
@NotNull 必須不為 null
@AssertTrue 必須為 true
@AssertFalse 必須為 false
@Min(value) 大於等於 給定數字
@Max(value) 小於等於 給定數字
@DecimalMin(value) 大於等於 給定數值
@DecimalMax(value) 小於等於 給定數字
@Size(max=, min=) 集合或字串長度在指定範圍內
@Digits 指定整數部分和小數部分可以接受的最大位數
@Past 必須為一個過去的時間
@Future 必須為一個未來的時間
@Pattern(regex=,flag=) 給定字串符合正規表示式
Hibernate Validator提供的校驗註解
@NotBlank(message =) 非 null,且長度必須大於 0
@Email 符合電子郵箱規範
@Length(min=,max=) 字串長度在給定範圍內
@NotEmpty 字串或集合 非空
@Range(min=,max=,message=) 數字或字串表示的數字在指定範圍內

自定義

當業務需要一些官方沒有提供的校驗型別的話,為了方便,我們就需要考慮使用自定義註解的形式了。


這裡,我們採用手機號的形式來演示一下,首先我們自定義一個註解:

@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {PhoneValidation.class})
public @interface PhoneValidationAnnotation {
    String message() default "手機號不符合規範格式";
}


在程式碼中,我們定義了預設的錯誤提示資訊。


然後,我們寫一個實現類,來具體實現註解所要表達的含義:

public class PhoneValidation implements ConstraintValidator<PhoneValidationAnnotation, String> {
    String phonePattern;
    Pattern compile;


    @Override
    public void initialize(PhoneValidationAnnotation constraintAnnotation) {
        phonePattern = "1[3456789]\\d{9}";
        compile = Pattern.compile(phonePattern);
    }

    @Override
    public boolean isValid(String value, ConstraintValidatorContext context) {
        return compile.matcher(value).matches();
    }
}

在程式碼中,我們建立了一個類,實現 ConstraintValidator 介面,並重寫其的初始化方法和驗證方法,這樣,在驗證引數的時候,其就會自動呼叫相關的方法來驗證傳遞的是否正確。

// 註解:要校驗的數字在給定的集合中

// 定義介面
@Target({ElementType.FIELD})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Constraint(validatedBy = {EnumValidation.class})
public @interface EnumValidationAnnotation {
    long[] limitValue() default {};
}

// 具體實現
public class EnumValidation implements ConstraintValidator<EnumValidationAnnotation, Long> {

    private long[] longValues;

    @Override
    public void initialize(EnumValidationAnnotation constraintAnnotation) {
        longValues = constraintAnnotation.limitValue();
    }

    @Override
    public boolean isValid(Long value, ConstraintValidatorContext context) {
        for (long longValue : longValues) {
            if (value == longValue) {
                return true;
            }
        }
        return false;
    }
}

相關文章