後端驗證,需要引入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>
分組校驗
在需要驗證的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