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 |
符合電子郵箱規范 | |
@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;
}
}