SpringMVCJSR-303規範進行校驗簡單示例

後端驗證,需要引入validation-api-2.0.1.GA.jar、hibernate-validator-6.0.10.Final.jar和jboss-logging-3.3.2.Final.jar,具體用什麼版本的jar包自行選擇。

jstl.jar、standard.js用於jsp頁面的標籤引用

springmvc配置文件

<!-- 開啓springmvc註解 -->
<mvc:annotation-driven validator="validator" />

<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean">
  <property name="providerClass"  value="org.hibernate.validator.HibernateValidator" />
  <!--不設置則默認爲classpath下的 ValidationMessages.properties -->
  <property name="validationMessageSource" ref="validationMessageSource" />
</bean>

<bean id="validationMessageSource" class="org.springframework.context.support.ReloadableResourceBundleMessageSource">  
  <property name="basename" value="/WEB-INF/static/i18n/validateMessages" />  
  <property name="fileEncodings" value="utf-8" />  
  <property name="cacheSeconds" value="120" />
  <property name="useCodeAsDefaultMessage" value="false" />  
</bean>

useCodeAsDefaultMessage設置爲false,如果爲true則如下面的用戶姓名字段會提示用戶姓名長度爲min~max個字符,佔位符的值無法被替換;

Controller相關設置,在model前添加@Valid註解;Controller的方法中@valid對應的@ModelAttribute參數與bindingResult之間不能有參數,它們必須緊挨在一起,否則報錯;(我之前就是在它們之間有HttpServletRequest參數,一直報400錯誤;當然你可以去掉model中驗證規則的註解,只是驗證將不起作用;)

@RequestMapping(value="/create", method=RequestMethod.POST)
public String createFormHandle(@Valid @ModelAttribute("formModel") UserModel formModel, BindingResult bindingResult) {
	if (bindingResult.hasErrors()) {
		return "sys/user/create";
	}

	try {
	    User entity = new User();
	    BeanUtils.copyProperties(formModel, entity);
	    CurrentUser currentUser = getCurrentUser();
	    entity.setCreateBy(currentUser.getUserId());
            entity.setCreateDatetime(new Date());
	    userService.insert(entity);
	} catch (Exception e) {
	    bindingResult.reject("User.exists", "User已經存在");
	    return "sys/user/create";
	}
	return String.format("redirect:/%s", "sys/user/list";);
}

jsp頁面內容設置,這裏舉個例子,不一一列舉。

引入:<%@ taglib uri="http://www.springframework.org/tags/form" prefix="sf" %>

<div>
    <input class="form-control" type="text" id="userName" name="userName" />
</div>
<sf:errors path="userName" cssClass="errorMsg"></sf:errors>

驗證元素註解

Bean Validation 中內置的 constraint
@Null   被註釋的元素必須爲 null(用於對象,如包裝類型Integer、Double、Date等)
@NotNull    被註釋的元素必須不爲 null(用於對象,如包裝類型Integer、Double、Date等)
@AssertTrue     被註釋的元素必須爲 true
@AssertFalse    被註釋的元素必須爲 false
@Min(value)     被註釋的元素必須是一個數字,其值必須大於等於指定的最小值(value爲整型long類型)
@Max(value)     被註釋的元素必須是一個數字,其值必須小於等於指定的最大值(value爲整型long類型)
@DecimalMin(value)  被註釋的元素必須是一個數字,其值必須大於等於指定的最小值
@DecimalMax(value)  被註釋的元素必須是一個數字,其值必須小於等於指定的最大值
@Size(max=, min=)   被註釋的元素的大小必須在指定的範圍內(不能用於整型)
@Digits (integer, fraction)     被註釋的元素必須是一個數字,其值必須在可接受的範圍內
@Past   被註釋的元素必須是一個過去的日期
@Future     被註釋的元素必須是一個將來的日期
@Pattern(regex=,flag=)  被註釋的元素必須符合指定的正則表達式
Hibernate Validator 附加的 constraint
@NotBlank(message =)   驗證字符串非null,且長度必須大於0
@Email  被註釋的元素必須是電子郵箱地址
@Length(min=,max=)  被註釋的字符串的大小必須在指定的範圍內
@NotEmpty   被註釋的字符串的必須非空
@Range(min=,max=,message=)  被註釋的元素必須在合適的範圍內(min和max爲整型long類型)

示例:驗證字符串

/**
* 用戶姓名.
*/
@NotBlank(message="用戶姓名不能爲空")
@Length(min=2, max=6, message="用戶姓名長度爲{min}~{max}個字符")
private String userName;	

/**
* 用戶姓名助記碼.
*/
@NotBlank(message="用戶姓名助記碼不能爲空")
@Length(max=6, message="用戶姓名助記碼最長爲{max}個字符")
private String mnemonic;

/**
* 登錄名.
*/
@NotBlank(message="")
@Length(min=2, max=6, message="登錄名不能爲空,且長度爲{min}~{max}個字符")
private String loginName;
		
/**
* 登錄密碼.
*/
@NotBlank(message="登錄密碼不能爲空")
@Length(max=6, message="登錄密碼最長爲{max}個字符")
private String loginPwd;	

/**
* 備註信息.
*/
@Length(max=6, message="備註最長{max}個字符")
private String remark;
jsp驗證信息


字符串必填,增加@NotBlank註解。

用戶姓名同時驗證必填和字符串長度,出現兩行驗證信息;這種情況請參考登錄名,將@NotBlank的message設置爲空,驗證信息全寫在@Length的message上。

示例:驗證數值

HV000030: No validator could be found for constraint 'javax.validation.constraints.Size' validating type 'java.lang.Integer'. Check configuration for 'sex'

字段上添加@Size註解,不論字段是否包裝類都報該錯誤,基本類型的始終被當做包裝類型,與是否加@NotNull和@NotBlank無關。原來是@Size只能用於String、Collection、Map和Array(測試過String類型,提示信息 個數在{min}~{max}之間,這個也是搞不懂);


整型數值

/**
 * 年齡
 */
@NotNull(message="年齡不能爲空")
@Range(min=18, max=65, message="年齡範圍爲{min}~{max}歲")
private int age;
	
/**
 * 性別
 */
@NotNull(message="性別不能爲空")
@Range(min=2, max=9, message="性別取值範圍爲{min}~{max}")
private Integer sex;

/**
 * 狀態(0:停用;1:正常;2:鎖定;).
 */
@NotNull(message="狀態不能爲空")
@Min(value=0, message="狀態不能小於{value}")
@Max(value=2, message="狀態不能大於{value}")
private Integer status;
jsp驗證信息


上面的年齡用基本類型,報錯,空字符串無法轉成int,修改爲如性別的包裝類型;

如:性別爲null,則不進行值範圍的驗證;不爲null才驗證值範圍。

示例:浮點型

@NotNull(message="工資不能爲空")
@DecimalMin(value="1234.5", inclusive=true, message="工資必須大於或等於{value}")
@DecimalMax(value="6789.0", inclusive=false, message="工資必須小於{value}")
@Digits(integer=4, fraction=1)
private Double salary;


@DecimalMin和@DecimalMax只能設置值大小,不能限定小數位數;inclusive爲true則包含某指,爲false則不包含;

@Digits的integer表示整數位數,fraction表示小數位數;

@Digits只能設置整數位數和小數位數,而不能設置某些具體值範圍(如三位數,但實際使用可能就不是剛好要100到999,可能能是更細的範圍);

所以浮點型的在必要時候爲@DecimalMin、@DecimalMax和@Digits的組合使用。

示例:日期

/**
 * 出生日期.
 */
@DateTimeFormat(pattern = "yyyy-MM-dd")
@NotNull(message="出生日期不能爲空")
@Past
private Date birthday;

/**
* 創建時間.
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
@NotNull(message="創建時間不能爲空")
@PastOrPresent
private Date createDatetime;
	
/**
* 更新時間.
*/
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
@NotNull(message="更新時間不能爲空")
@Future
private Date updateDatetime;

/**
 * 更新時間1.
 */
@DateTimeFormat(pattern = "yyyy-MM-dd HH:mm")
@NotNull(message="更新時間1不能爲空")
@FutureOrPresent
private Date updateDatetime1;

日期類型須配合@DateTimeFormat使用,否則提交時會提示String不能轉爲Date;

經測試

@Past需要一個過去的時間(爲現在時間仍會出現錯誤提示)

@PastOrPresent需要一個過去或現在的時間

@Future需要一個將來的時間

@FutureOrPresent需要一個將來或現在的時間(爲現在時間仍會出現錯誤提示)

示例:正則表達式

/**
* 登錄密碼.
*/
@Pattern(regexp="^[a-zA-Z0-9_]{6,20}$", message="登錄密碼只能包含字符、數字、下劃線,且字符串長度爲6~20")
private String loginPwd;

2. 對象級聯校驗

只須在關聯對象屬性上加上@Valid

public class UserModel {
	/**
	* 用戶姓名.
	*/
	@NotBlank(message="用戶姓名不能爲空")
	@Length(min=2, max=6, message="用戶姓名長度爲{min}~{max}個字符")
	private String userName;	
	
	public String getUserName() {
		return userName;
	}
	
	public void setUserName(String userName) {
		this.userName = userName;
	}
	
	/**
	 * 用戶其他信息
	 */
	@Valid
	private UserExtModel userExtModel;
	
	public UserExtModel getUserExtModel() {
		return userExtModel;
	}

	public void setUserExtModel(UserExtModel userExtModel) {
		this.userExtModel = userExtModel;
	}
}

public class UserExtModel {
	private Integer userId;

	public Integer getUserId() {
		return userId;
	}
	
	public void setUserId(Integer userId) {
		this.userId = userId;
	}
	
	@NotBlank(message="聯繫電話不能爲空")
	private String phone;

	public String getPhone() {
		return phone;
	}

	public void setPhone(String phone) {
		this.phone = phone;
	}
	
	private String userIdCard;
	
	public String getUserIdCard() {
		return userIdCard;
	}

	public void setUserIdCard(String userIdCard) {
		this.userIdCard = userIdCard;
	}
}

注意關聯對象字段的引用

<div>
	<label for="phone">聯繫電話</label>
	<div>
		<input class="form-control" type="text" id="phone" name="userExtModel.phone"" />
	</div>
	<sf:errors path="userExtModel.phone" cssClass="errorMsg"></sf:errors>
</div>

<div>
	<label for="userIdCard">身份證</label>
	<div>
		<input class="form-control" type="text" id="userIdCard" name="userExtModel.userIdCard" />
	</div>
	<sf:errors path="userExtModel.userIdCard" cssClass="errorMsg"></sf:errors>
</div>


@ConvertGroup不知道怎麼用

分組校驗


在需要驗證的Controller加上@Validated,裏面爲驗證的分組(分組內容);

public interface GroupA {

}

public interface GroupB {

}

model

@NotBlank(message="用戶姓名不能爲空", groups={Default.class})
@Length(min=2, max=6, message="用戶姓名長度爲{min}~{max}個字符", groups={GroupA.class})
private String userName;

@NotBlank(message="身份證號不能爲空", groups={GroupB.class})
@Length(min=15, message="身份證最小長度{min}")
private String userIdCard;

如果添加了分組(groups),需要驗證的每個註解都要加上對應分組(每個註解上的groups爲數組,可添加多個分組);

任何分組都要驗證的註解,要加groups={Default.class}否則不驗證該註解內容。

自定義驗證器

示例:性別(GB/T 2261.1-2003(0:未知的性別;1:男性;2:女性;5:女性改(變)爲男性;6:男性改(變)爲女性;9:未說明的性別;));這個沒法用數值範圍;(參考:https://blog.csdn.net/poortess/article/details/78136170)

性別枚舉

/**
 * 性別(GB/T 2261.1-2003(0:未知的性別;1:男性;2:女性;5:女性改(變)爲男性;6:男性改(變)爲女性;9:未說明的性別;)).
 * @author chensan
 *
 */
public enum GenderEnum {
	/**
	 * 未知的性別
	 */
	UNKNOWN(0),
	/**
	 * 男性
	 */
	MALE(1),
	/**
	 * 女性
	 */
	FEMALE(2),
	/**
	 * 女性改(變)爲男性
	 */
	FEMALE_TO_MALE(5),
	/**
	 * 男性改(變)爲女性
	 */
	MALE_TO_FEMALE(6),
	/**
	 * 未說明的性別
	 */
	UNSTATED(9);
	
	private Integer gender;
	
	private GenderEnum(Integer gender) {
		this.gender = gender;
	}
	
	@JsonValue
	public Integer getGender() {
		return this.gender;
	}
	
	@JsonCreator
	public static boolean isInEnum(Integer sex) {
		if (sex == null) {
			return true;
		}
		for (GenderEnum gender : GenderEnum.values()) {
			if (gender.gender.equals(sex)) {
				return true;
			}
		}
		return false;
	}
}

自定義Validator

/**
 * 自定義性別Validator
 */
public class CheckGenderValidator implements ConstraintValidator<CheckGenderConstraint, Integer> {
    @Override
    public void initialize(CheckGenderConstraint constraintAnnotation) {
    }

    @Override
    public boolean isValid(Integer value, ConstraintValidatorContext context) {
    	return GenderEnum.isInEnum(value);
    }
}

驗證器註解

/**
 * 用枚舉指定參數
 */
@Target({ElementType.FIELD, ElementType.METHOD, ElementType.ANNOTATION_TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Constraint(validatedBy = CheckGenderValidator.class)
@Documented
public @interface CheckGenderConstraint {
    /**
     * 用來定義默認得消息模版, 當這個約束條件被驗證失敗的時候,通過此屬性來輸出錯誤信息.
     * @return
     */
    String message() default "請填寫正確的性別";

    /**
     * 用於指定這個約束條件屬於哪(些)個校驗組
     * @return
     */
    Class<?>[] groups() default {};

    /**
     * Bean Validation API 的使用者可以通過此屬性來給約束條件指定嚴重級別. 這個屬性並不被API自身所使用.
     * @return
     */
    Class<? extends Payload>[] payload() default {};
}

model字段引入自定義驗證器註解(如果自定義驗證內容參與分組,如下添加groups)

@NotNull(message="性別不能爲空")
@CheckGenderConstraint(groups={GroupB.class})
private Integer sex;

model方式校驗應用於web,接口校驗則需要用到hibernate的校驗模式(後續用到 會添加)。

問題:

當某字段不爲空或爲某值,纔可以(且必須)填寫某些內容,這些內容才驗證規則,這種根據實際情況判斷是否參與驗證,如何動態選擇驗證器分組呢(或分組能解決嗎)。

文章參考:https://www.cnblogs.com/mr-yang-localhost/p/7812038.html









發佈了102 篇原創文章 · 獲贊 49 · 訪問量 33萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章