一:hibernate-validator 基礎
1. 簡介:
通過使用註解Annotations 給類或者類的屬性加上約束(constraint),在運行期檢查屬性值的合法性.
2. 作用:
在API接口開發中參數校驗是非常重要的事情,因為客戶端很可能會少傳參數,或者值不合法,甚至參數值是惡意的,所以對客戶端傳來的參數的合法性就必須要校驗了,其中將參數值的校驗規則通過註解的形式註解到屬性上是一種比較優雅的方式。
3. 常用的約束註解
4. 初識hibernate-validator
public class Address { @NotNull private String line1; private String line2; private String zip; private String state; @Length(max = 20) @NotNull private String country; @Range(min = -2, max = 50, message = "Floor out of range") public int floor; // getter&&setter}二:整合校驗hibernate-validator
該示例是在SpringMVC+FastJson整合(//www.VeVB.COm/article/139094.htm)基礎上進行集成的,可以先看一下這篇文章(有源碼供下載),在該文章的基礎上整合hibernate-validator
整合步驟:
1、在pom.xml中引入hibernate-validator依賴
<dependency> <groupId>org.hibernate</groupId> <artifactId>hibernate-validator</artifactId> <version>5.4.1.Final</version></dependency>
2、在[xxx]-servlet.xml中配置驗證器:HibernateValidator
<!-- <mvc:annotation-driven> 增加驗證器屬性validator="validator" --><mvc:annotation-driven validator="validator"> <mvc:message-converters register-defaults="true"> <!-- 配置Fastjson 替換原來的jackson支持--> <bean> <property name="supportedMediaTypes"> <list> <value>text/html;charset=UTF-8</value> <value>application/json</value> </list> </property> <property name="features"> <list> <value>QuoteFieldNames</value> <value>WriteMapNullValue</value> </list> </property> </bean> </mvc:message-converters> </mvc:annotation-driven> <bean id="validator"> <property name="providerClass" value="org.hibernate.validator.HibernateValidator"/> <property name="validationMessageSource" ref="messageSource"/> </bean> <bean id="messageSource"> <property name="basenames"> <list> <value>classpath:conf/settings/validation</value> <value>classpath:org/hibernate/validator/ValidationMessages</value> </list> </property> <property name="useCodeAsDefaultMessage" value="false"/> <property name="defaultEncoding" value="UTF-8"/> <property name="cacheSeconds" value="60"/> </bean>
3、在src/main/resources/conf/settings位置定義驗證消息文件描述validation.properties
validation.common.not.null=該字段不能為空validation.common.not.range=長度非法validation.common.format.error=格式錯誤validation.param.age=年齡未滿18周歲rep.error.unknown=未知錯誤
4、新建實體類以供測試
UserEntity
package com.mengdee.manage.validator;import javax.validation.constraints.Min;import javax.validation.constraints.NotNull;import javax.validation.constraints.Null;import javax.validation.constraints.Pattern;import org.hibernate.validator.constraints.Email;import org.hibernate.validator.constraints.Length;import org.hibernate.validator.constraints.NotBlank;public class UserEntity { @Null(groups={GroupA.class}) @NotNull(groups={GroupB.class}) @Min(value = 1, message="id值必須大於0", groups={GroupB.class}) private Long id; @NotBlank(groups={GroupA.class, GroupB.class}) @Pattern(regexp="^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$", message="由6-21字母和數字組成,不能是純數字或純英文", groups={GroupA.class, GroupB.class}) private String password; @NotBlank(groups={GroupA.class, GroupB.class}) @Pattern(regexp="^((13[0-9])|(15[^4,//D])|(18[0,3-9]))//d{8}$", message="手機號格式不正確") private String phone; @NotBlank(groups={GroupB.class}) @Length(min=6, max=12, message="暱稱長度為6到12位") private String nickname; @Min(value=18, message="{validation.param.age}") private int age; @NotBlank(groups={GroupA.class}) @Email(message="{validation.common.format.error}") private String email; @NotBlank @Length(min=3, max=10, message="{validation.common.not.range}") private String username; public UserEntity() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; }}UserModel
package com.mengdee.manage.validator;import javax.validation.constraints.Min;import javax.validation.constraints.Pattern;import org.hibernate.validator.constraints.Email;import org.hibernate.validator.constraints.Length;import org.hibernate.validator.constraints.NotBlank;public class UserModel { @Min(value = 1, message="id值必須大於0") private long id; @NotBlank @Length(min=6, max=12, message="暱稱長度為6到12位") private String nickname; @Min(value=18, message="{validation.param.age}") private int age; @NotBlank @Email(message="{validation.common.format.error}") private String email; @NotBlank @Pattern(regexp="^(?![0-9]+$)(?![a-zA-Z]+$)[0-9A-Za-z]{6,20}$", message="由6-21字母和數字組成,不能是純數字或純英文") private String password; @NotBlank @Length(min=3, max=10, message="{validation.common.not.range}") private String username; @NotBlank @Pattern(regexp="^((13[0-9])|(15[^4,//D])|(18[0,3-9]))//d{8}$", message="手機號格式不正確") private String phone; public UserModel() { } public long getId() { return id; } public void setId(long id) { this.id = id; } public String getNickname() { return nickname; } public void setNickname(String nickname) { this.nickname = nickname; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } public String getEmail() { return email; } public void setEmail(String email) { this.email = email; } public String getPassword() { return password; } public void setPassword(String password) { this.password = password; } public String getUsername() { return username; } public void setUsername(String username) { this.username = username; } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; } }UserDetail :
package com.mengdee.manage.validator;import org.hibernate.validator.constraints.NotBlank;public class UserDetail { private Long id; @NotBlank private String address; @NotBlank private String company; public UserDetail() { } public Long getId() { return id; } public void setId(Long id) { this.id = id; } public String getAddress() { return address; } public void setAddress(String address) { this.address = address; } public String getCompany() { return company; } public void setCompany(String company) { this.company = company; }}使用ValidController 進行測試
package com.mengdee.manage.controller;import javax.servlet.http.HttpServletRequest;import javax.validation.Valid;import org.springframework.stereotype.Controller;import org.springframework.validation.BindingResult;import org.springframework.validation.FieldError;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import com.mengdee.manage.validator.UserModel;@Controller@RequestMapping("/Valid")public class ValidController extends BaseController { // http://localhost:8081/platform-springmvc-webapp/Valid/test?age=18&nickname=mengdee&id=1&[email protected]&password=root123&username=123&phone=18321758957 @RequestMapping(value = "/test", method = RequestMethod.GET) public @ResponseBody Object validation(HttpServletRequest request, @Valid UserModel user, BindingResult bindingResult){ if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); return super.ResponseJsonError(fieldError); } return "ok"; } // 一個方法同時校驗多個對象需要綁定多個結果BindingResult,出現一個@Valid就對應後面聲明的一個BindingResult @RequestMapping(value = "/test2", method = RequestMethod.GET) public @ResponseBody Object validationMore(HttpServletRequest request, @Valid UserModel user, BindingResult bindingResult, @Valid UserDetail userDetail, BindingResult bindingResult2){ if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); return super.ResponseJsonError(fieldError); } if (bindingResult2.hasErrors()) { FieldError fieldError = bindingResult2.getFieldError(); return super.ResponseJsonError(fieldError); } return "ok"; }}使用注意:
1、對於多個字段,系統驗證的順序好像和字段聲明的順序是不一樣的,好像是無須的
2、對於每個驗證如果沒有message屬性,系統會使用默認的,如對應@NotBlank相當於@NotBlank(message=”不能為空”)
3、對於引用類型如String等類型,一定要結合@NotNull、@NotEmpty或者@NotBlank來配合使用,如果不寫空的約束,經測試,該字段是不參與校驗的,如單獨使用@Pattern、@Length、@Email等是不會進行驗證的,必須要使用空的約束來限制
@Min:用於基本數據類型,如int、long等
@NotNull: 任何對象的value不能為null
@NotEmpty:集合對象的元素不為0,即集合不為空,也可以用於字符串不為null
@NotBlank:只能用於字符串不為null,並且字符串trim()以後length要大於0
三:分組校驗@Validated
1. 分組的作用(使用場景):
每個校驗的註解約束都有一個groups屬性,用於指定該約束是屬於哪個組的,這樣在同一個字段上就可以配置多套約束,在使用的時候只需要指定使用那套約束即可,例如對於註冊用戶和修改用戶信息時,註冊時id必須為空,修改用戶信息時id必須不能為空,在使用的時候只需要將這兩種約束分配到不同的組中即可,如添加時使用組A的約束,更新時使用組B的約束
2. 分組就是一個空接口interface
GroupA 和GroupB
package com.mengdee.manage.validator;public interface GroupA {}package com.mengdee.manage.validator;public interface GroupB {}在使用時指定具體使用那套分組的約束@Validated({GroupA.class})
3. 組序列@GroupSequence
默認情況下,不同組別的約束驗證是無序的,組序列就是按照分組的前後順序依次驗證,如先驗證GroupA組的約束,再驗證GroupB組的約束。如果對組的校驗順序有要求,例如必須先校驗A組再校驗B組,可以使用@GroupSequence來定義每個組的順序
使用場景:
(1)第二個組中的約束驗證依賴於一個穩定狀態來運行,而這個穩定狀態是由第一個組來進行驗證的。
(2)某個組的驗證比較耗時,CPU 和內存的使用率相對比較大,最優的選擇是將其放在最後進行驗證。因此,在進行組驗證的時候尚需提供一種有序的驗證方式,這就提出了組序列的概念。
一個組可以定義為其他組的序列,使用它進行驗證的時候必須符合該序列規定的順序。在使用組序列驗證的時候,如果序列前邊的組驗證失敗,則後面的組將不再給予驗證。
使用註解GroupSequence定義組序列:GroupAB
package com.mengdee.manage.validator;import javax.validation.GroupSequence;@GroupSequence({GroupA.class, GroupB.class})public interface GroupAB {ValidationController
package com.mengdee.manage.controller;import javax.servlet.http.HttpServletRequest;import org.springframework.stereotype.Controller;import org.springframework.validation.BindingResult;import org.springframework.validation.FieldError;import org.springframework.validation.annotation.Validated;import org.springframework.web.bind.annotation.RequestMapping;import org.springframework.web.bind.annotation.RequestMethod;import org.springframework.web.bind.annotation.ResponseBody;import com.mengdee.manage.validator.GroupA;import com.mengdee.manage.validator.GroupAB;import com.mengdee.manage.validator.GroupB;import com.mengdee.manage.validator.UserEntity;@Controller@RequestMapping("/validated")public class ValidationController extends BaseController { // 校驗時指定了GroupA,那麼只校驗約束中包含GroupA的約束,沒有包含就不校驗,例如對於phone, @NotBlank指定的分組,而@Pattern沒有指定分組,那麼只校驗空著一個約束,不校驗手機號格式// http://localhost:8081/platform-springmvc-webapp/validated/groupA?password=root123&phone=123 @RequestMapping(value = "/groupA", method = RequestMethod.GET) public @ResponseBody Object groupA(HttpServletRequest request, @Validated({GroupA.class}) UserEntity user, BindingResult bindingResult){ if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); return this.ResponseJsonError(fieldError); } return "ok"; } // http://localhost:8081/platform-springmvc-webapp/validated/groupB?phone=123&password=root123&id=1 @RequestMapping(value = "/groupB", method = RequestMethod.GET) public @ResponseBody Object groupB(HttpServletRequest request, @Validated({GroupB.class}) UserEntity user, BindingResult bindingResult){ if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); return this.ResponseJsonError(fieldError); } return "ok"; } // groupAB // http://localhost:8081/platform-springmvc-webapp/validated/groupAB?phone=111&password=root123&nickname=123&[email protected] // @Validated({GroupA.class, GroupB.class}):GroupA和GroupB的關係是或的關係,就像數據庫中的OR一樣,只要滿足一個條件就會對該約束進行校驗,同時使用多個組注意多個組之間沒有先後屬性之說,並不是先校驗組A,然後再校驗組B // 因為id的為空和不為空的約束都會進行檢查,所以先註釋掉該屬性@RequestMapping(value = "/groupAB", method = RequestMethod.GET) public @ResponseBody Object groupAB(HttpServletRequest request, @Validated({GroupA.class, GroupB.class}) UserEntity user, BindingResult bindingResult){ if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); return this.ResponseJsonError(fieldError); } return "ok"; } // default // http://localhost:8081/platform-springmvc-webapp/[email protected]&age=18 // @Validated 如果沒有指定groups則驗證沒有分組的屬性(此時和@Valid功能一樣),如果一個字段上有多個約束,都必須沒有指定組,如果部分約束指定的組,部分約束沒有指定約束,那麼在使用@Validated時不進行檢查的@RequestMapping(value = "/default", method = RequestMethod.GET) public @ResponseBody Object defaultGroup(HttpServletRequest request, @Validated UserEntity user, BindingResult bindingResult){ if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); return this.ResponseJsonError(fieldError); } return "ok"; } //localhost:8081/platform-springmvc-webapp/validated/sequence?phone=123&password=root123&email=123&nickname=123 // 對於一個屬性上有多個約束,並且多個約束不都在同一個組,那麼在檢查的時候順序是根據GroupSequence定義的先後順序來檢查的@RequestMapping(value = "/sequence", method = RequestMethod.GET) public @ResponseBody Object sequence(HttpServletRequest request, @Validated({GroupAB.class}) UserEntity user, BindingResult bindingResult){ if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); return this.ResponseJsonError(fieldError); } return "ok"; }}四:自定義hibernate validation註解
當hibernate validation提供的註解不能滿足需求時,可以自定義校驗約束。
自定義註解約束步驟:
創建註解@Phone
package com.mengdee.manage.validator;import java.lang.annotation.Target;import javax.validation.Constraint;import javax.validation.Payload;import static java.lang.annotation.ElementType.ANNOTATION_TYPE;import static java.lang.annotation.ElementType.CONSTRUCTOR;import static java.lang.annotation.ElementType.FIELD;import static java.lang.annotation.ElementType.METHOD;import static java.lang.annotation.ElementType.PARAMETER;import static java.lang.annotation.RetentionPolicy.RUNTIME;import java.lang.annotation.Documented;import java.lang.annotation.Retention;@Target({METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER })@Retention(RUNTIME)@Documented@Constraint(validatedBy = {PhoneConstraint.class})public @interface Phone { String message() default "手機號格式錯誤"; String regexp() default "^((13[0-9])|(15[^4,//D])|(18[0,3-9]))//d{8}$"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default { }; @Target({ METHOD, FIELD, ANNOTATION_TYPE, CONSTRUCTOR, PARAMETER }) @Retention(RUNTIME) @Documented public @interface List { Phone[] value(); } }創建手機號註解對應的約束驗證類PhoneConstraint
package com.mengdee.manage.validator;import javax.validation.ConstraintValidator;import javax.validation.ConstraintValidatorContext;public class PhoneConstraint implements ConstraintValidator<Phone, String> { private String regexp; @Override public void initialize(Phone phoneAnnotation) { this.regexp = phoneAnnotation.regexp(); } @Override public boolean isValid(String value, ConstraintValidatorContext context) { if (value == null) { return true; // HV000028: Unexpected exception during isValid call } if (value.matches(regexp)) { return true; } return false; }}在屬性上使用註解@Phone
package com.mengdee.manage.validator;import org.hibernate.validator.constraints.NotBlank;public class UserDetail { @NotBlank @Phone private String phone; public UserDetail() { } public String getPhone() { return phone; } public void setPhone(String phone) { this.phone = phone; }}測試註解
// http://localhost:8081/platform-springmvc-webapp/Valid/test3?address=123&company=456&phone=123 @RequestMapping(value = "/test3", method = RequestMethod.GET) public @ResponseBody Object validationCustomAnnotation(HttpServletRequest request, @Valid UserDetail userDetail, BindingResult bindingResult){ if (bindingResult.hasErrors()) { FieldError fieldError = bindingResult.getFieldError(); return super.ResponseJsonError(fieldError); } return "ok"; }完整代碼下載:http://xiazai.VeVB.COm/201804/yuanma/platform-springmvc-webapp(VeVB.COm).rar
總結
以上就是這篇文章的全部內容了,希望本文的內容對大家的學習或者工作具有一定的參考學習價值,如果有疑問大家可以留言交流,謝謝大家對武林網的支持。
參考文章: