spring @validated校驗,分組校驗,自定義校驗

嚴謹的接口是必須要有參數校驗的,迴避掉一些不合理的請求。但是校驗邏輯和正常業務邏輯摻雜在一起,固然能完成校驗需求,但是在實現方式上卻不那麼優雅。比如現在有一個user註冊接口:

1.直男癌一般的校驗

@PostMapping("/user")
	public Mono<String> insert(@RequestBody User user) {
		if(StringUtils.isEmpty(user.getName())) {
			return Mono.just("姓名不能爲空");
		}
		if(StringUtils.isEmpty(user.getEnname())) {
			return Mono.just("英文姓名不能爲空");
		}
		// 處理業務邏輯
		return Mono.just("註冊成功");
	}

這種最爲無腦直接,也確實能完成校驗需求,但是卻有點憨憨,當一個對象中有很多字段需要校驗的時候,很多if - else就顯得很臃腫。我剛寫代碼的時候就經常這樣幹。。。。

2.藉助spring的@validated註解來校驗

@Data
public class User {

	private int id;
	
	@NotNull(message = "姓名不可爲空")
	private String name;
	
	@NotNull(message = "英文姓名不可爲空")
	private String enname;
	
	private String mobile;
	
}


@PostMapping("/user")
	public Mono<String> insert(@RequestBody @Validated User user) {
		
		// 處理業務邏輯
		return Mono.just("註冊成功");
	}

藉助了spring的力量之後,原先臃腫的if - else沒有了,代碼瞬間變的簡潔了起來。但是這樣子也是有一些問題的,比如我們現在要在Controller中新增添一個修改user的接口,既然是修改接口,我們可以選擇只修改user的name或者只修改user的enname。那麼就會出現一個問題,新增接口的校驗和修改接口的校驗衝突了。新增接口要求:name和enname都不能爲空,修改接口要求:name和enname可以爲空,這個時候用一個user對象就有點尷尬。

3.@validated 分組校驗


@Data
public class User {

	@Min(value = 1, message = "ID不可爲空", groups = {Update.class})
	private int id;
	
	@NotNull(message = "姓名不可爲空", groups = {Insert.class})
	private String name;
	
	@NotNull(message = "英文姓名不可爲空", groups = {Insert.class})
	private String enname;
	
	private String mobile;
	
}


@PostMapping("/user")
	public Mono<String> insert(@RequestBody @Validated(Insert.class) User user) {
		// 處理新增邏輯
		return Mono.just("註冊成功");
	}
	
	@PutMapping("/user")
	public Mono<String> update(@RequestBody @Validated(Update.class) User user) {
		// 處理修改邏輯
		return Mono.just("修改成功");
	}

// 隨便自定義2個接口 用於標識新增和修改	
public interface Insert{
	
}
public interface Update{
	
}

通過@Validated的分組校驗,可以指定在什麼時候才驗證字段的合法性。一切看起來都是這麼的完美,spring爲我們整理了一些內置的驗證註解,部分如下:

@Null  被註釋的元素必須爲null
@NotNull  被註釋的元素不能爲null
@AssertTrue  被註釋的元素必須爲true
@AssertFalse  被註釋的元素必須爲false
@Min(value)  被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@Max(value)  被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@DecimalMin(value)  被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value)  被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max,min)  被註釋的元素的大小必須在指定的範圍內。
@Digits(integer,fraction)  被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past  被註釋的元素必須是一個過去的日期
@Future  被註釋的元素必須是一個將來的日期
@Pattern(value) 被註釋的元素必須符合指定的正則表達式。
@Email 被註釋的元素必須是電子郵件地址
@Length 被註釋的字符串的大小必須在指定的範圍內
@NotEmpty  被註釋的字符串必須非空
@Range  被註釋的元素必須在合適的範圍內

這些自帶的驗證註解包括驗證長度,驗證是否爲true,驗證是否爲郵箱,驗證最大值等等。但是現在我們的user對象又有一個新的需求,添加電話號碼字段,但是spring並沒有一個自帶的註解是可以驗證一個字符串是否是電話號碼的。

4.自定義驗證註解

第一步,新增一個註解類:

import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

import javax.validation.Constraint;
import javax.validation.Payload;

@Target({ ElementType.ANNOTATION_TYPE, ElementType.METHOD, ElementType.FIELD, ElementType.CONSTRUCTOR,
		ElementType.PARAMETER })
//約束註解應用的目標元素類型(METHOD, FIELD, TYPE, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER等)
@Retention(RetentionPolicy.RUNTIME) // 約束註解應用的時機
@Documented
@Constraint(validatedBy = MobileValidator.class)
public @interface IsMobile {
	
	boolean required() default true;

	String message() default "手機號格式不正確";

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

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

}

 

第二步,編寫校驗類,實現ConstraintValidator<A extends Annotation, T>接口。ConstraintValidator<A extends Annotation, T>是個泛型接口,第一個泛型是註解類,第二個泛型是需要驗證的數據類型。接口有2個方法,一個initialize負責初始化,另外一個isValid負責具體的校驗邏輯。

import javax.validation.ConstraintValidator;
import javax.validation.ConstraintValidatorContext;

public class MobileValidator implements ConstraintValidator<IsMobile, String>{

	@Override
	public void initialize(IsMobile constraintAnnotation) {
		ConstraintValidator.super.initialize(constraintAnnotation);
	}
	
	@Override
	public boolean isValid(String value, ConstraintValidatorContext context) {
		// 關於手機號的驗證偷個懶 不爲空且長度不是11位的字符串 就被認爲是非法
		if(value != null && value.length() != 11) {
			System.out.println("手機號非法");
			return false;
		}
		return true;
	}

}

第三步, 使用自定義註解:


import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;

import com.arvato.ann.Insert;
import com.arvato.ann.IsMobile;
import com.arvato.ann.Update;

import lombok.Data;

@Data
public class User {

	@Min(value = 1, message = "ID不可爲空", groups = {Update.class})
	private int id;
	
	@NotNull(message = "姓名不可爲空", groups = {Insert.class})
	private String name;
	
	@NotNull(message = "英文姓名不可爲空", groups = {Insert.class})
	private String enname;
	
	@IsMobile(message = "手機號格式非法", groups = {Update.class, Insert.class})
	private String mobile;
	
}

至此,項目中大部分的參數驗證都能通過註解來完成了,但是也不排除有一些業務場景是校驗註解無法完成的,有時候還是需要一些直男癌的if-else的。

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