一、參數(shù)校驗(yàn)
公司主營(yíng)業(yè)務(wù):成都網(wǎng)站設(shè)計(jì)、做網(wǎng)站、成都外貿(mào)網(wǎng)站建設(shè)公司、移動(dòng)網(wǎng)站開發(fā)等業(yè)務(wù)。幫助企業(yè)客戶真正實(shí)現(xiàn)互聯(lián)網(wǎng)宣傳,提高企業(yè)的競(jìng)爭(zhēng)能力。創(chuàng)新互聯(lián)公司是一支青春激揚(yáng)、勤奮敬業(yè)、活力青春激揚(yáng)、勤奮敬業(yè)、活力澎湃、和諧高效的團(tuán)隊(duì)。公司秉承以“開放、自由、嚴(yán)謹(jǐn)、自律”為核心的企業(yè)文化,感謝他們對(duì)我們的高要求,感謝他們從不同領(lǐng)域給我們帶來的挑戰(zhàn),讓我們激情的團(tuán)隊(duì)有機(jī)會(huì)用頭腦與智慧不斷的給客戶帶來驚喜。創(chuàng)新互聯(lián)公司推出黃石免費(fèi)做網(wǎng)站回饋大家。
在開發(fā)中經(jīng)常需要寫一些字段校驗(yàn)的代碼,比如字段非空,字段長(zhǎng)度限制,郵箱格式驗(yàn)證等等,寫這些與業(yè)務(wù)邏輯關(guān)系不大的代碼個(gè)人感覺有兩個(gè)麻煩:
hibernate validator(官方文檔)提供了一套比較完善、便捷的驗(yàn)證實(shí)現(xiàn)方式。
spring-boot-starter-web包里面有hibernate-validator包,不需要引用hibernate validator依賴。
二、hibernate validator校驗(yàn)demo
先來看一個(gè)簡(jiǎn)單的demo,添加了Validator的注解:
import org.hibernate.validator.constraints.NotBlank; import javax.validation.constraints.AssertFalse; import javax.validation.constraints.Pattern;
@Getter @Setter @NoArgsConstructor public class DemoModel { @NotBlank(message="用戶名不能為空") private String userName; @NotBlank(message="年齡不能為空") @Pattern(regexp="^[0-9]{1,2}$",message="年齡不正確") private String age; @AssertFalse(message = "必須為false") private Boolean isFalse; /** * 如果是空,則不校驗(yàn),如果不為空,則校驗(yàn) */ @Pattern(regexp="^[0-9]{4}-[0-9]{2}-[0-9]{2}$",message="出生日期格式不正確") private String birthday; }
POST接口驗(yàn)證,BindingResult是驗(yàn)證不通過的結(jié)果集合:
@RequestMapping("/demo2") public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){ if(result.hasErrors()){ for (ObjectError error : result.getAllErrors()) { System.out.println(error.getDefaultMessage()); } } }
POST請(qǐng)求傳入的參數(shù):{"userName":"dd","age":120,"isFalse":true,"birthday":"21010-21-12"}
輸出結(jié)果:
出生日期格式不正確
必須為false
年齡不正確
參數(shù)驗(yàn)證非常方便,字段上注解+驗(yàn)證不通過提示信息即可代替手寫一大堆的非空和字段限制驗(yàn)證代碼。下面深入了解下參數(shù)校驗(yàn)的玩法。
三、hibernate的校驗(yàn)?zāi)J?/strong>
細(xì)心的讀者肯定發(fā)現(xiàn)了:上面例子中一次性返回了所有驗(yàn)證不通過的集合,通常按順序驗(yàn)證到第一個(gè)字段不符合驗(yàn)證要求時(shí),就可以直接拒絕請(qǐng)求了。Hibernate Validator有以下兩種驗(yàn)證模式:
1、普通模式(默認(rèn)是這個(gè)模式)
普通模式(會(huì)校驗(yàn)完所有的屬性,然后返回所有的驗(yàn)證失敗信息)
2、快速失敗返回模式
快速失敗返回模式(只要有一個(gè)驗(yàn)證失敗,則返回)
兩種驗(yàn)證模式配置方式:(參考官方文檔)
failFast:true 快速失敗返回模式 false 普通模式
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) .configure() .failFast( true ) .buildValidatorFactory(); Validator validator = validatorFactory.getValidator();
和 (hibernate.validator.fail_fast:true 快速失敗返回模式 false 普通模式)
ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) .configure() .addProperty( "hibernate.validator.fail_fast", "true" ) .buildValidatorFactory(); Validator validator = validatorFactory.getValidator();
四、hibernate的兩種校驗(yàn)
配置hibernate Validator為快速失敗返回模式:
@Configuration public class ValidatorConfiguration { @Bean public Validator validator(){ ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) .configure() .addProperty( "hibernate.validator.fail_fast", "true" ) .buildValidatorFactory(); Validator validator = validatorFactory.getValidator(); return validator; } }
1、請(qǐng)求參數(shù)校驗(yàn)
如demo里示例的,驗(yàn)證請(qǐng)求參數(shù)時(shí),在@RequestBody DemoModel demo之間加注解 @Valid,然后后面加BindindResult即可;多個(gè)參數(shù)的,可以加多個(gè)@Valid和BindingResult,如:
public void test()(@RequestBody @Valid DemoModel demo, BindingResult result) public void test()(@RequestBody @Valid DemoModel demo, BindingResult result,@RequestBody @Valid DemoModel demo2, BindingResult result2)
@RequestMapping("/demo2") public void demo2(@RequestBody @Valid DemoModel demo, BindingResult result){ if(result.hasErrors()){ for (ObjectError error : result.getAllErrors()) { System.out.println(error.getDefaultMessage()); } } }
2、GET參數(shù)校驗(yàn)(@RequestParam參數(shù)校驗(yàn))
使用校驗(yàn)bean的方式,沒有辦法校驗(yàn)RequestParam的內(nèi)容,一般在處理Get請(qǐng)求(或參數(shù)比較少)的時(shí)候,會(huì)使用下面這樣的代碼:
@RequestMapping(value = "/demo3", method = RequestMethod.GET) public void demo3(@RequestParam(name = "grade", required = true) int grade,@RequestParam(name = "classroom", required = true) int classroom) { System.out.println(grade + "," + classroom); }
使用@Valid注解,對(duì)RequestParam對(duì)應(yīng)的參數(shù)進(jìn)行注解,是無效的,需要使用@Validated注解來使得驗(yàn)證生效。如下所示:
a.此時(shí)需要使用MethodValidationPostProcessor
的Bean:
@Bean public MethodValidationPostProcessor methodValidationPostProcessor() { /**默認(rèn)是普通模式,會(huì)返回所有的驗(yàn)證不通過信息集合*/ return new MethodValidationPostProcessor(); }
或 可對(duì)MethodValidationPostProcessor
進(jìn)行設(shè)置Validator(因?yàn)榇藭r(shí)不是用的Validator進(jìn)行驗(yàn)證,Validator的配置不起作用)
@Bean public MethodValidationPostProcessor methodValidationPostProcessor() { MethodValidationPostProcessor postProcessor = new MethodValidationPostProcessor(); /**設(shè)置validator模式為快速失敗返回*/ postProcessor.setValidator(validator()); return postProcessor; } @Bean public Validator validator(){ ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) .configure() .addProperty( "hibernate.validator.fail_fast", "true" ) .buildValidatorFactory(); Validator validator = validatorFactory.getValidator(); return validator; }
b.方法所在的Controller上加注解@Validated
@RequestMapping("/validation") @RestController @Validated public class ValidationController { /**如果只有少數(shù)對(duì)象,直接把參數(shù)寫到Controller層,然后在Controller層進(jìn)行驗(yàn)證就可以了。*/ @RequestMapping(value = "/demo3", method = RequestMethod.GET) public void demo3(@Range(min = 1, max = 9, message = "年級(jí)只能從1-9") @RequestParam(name = "grade", required = true) int grade, @Min(value = 1, message = "班級(jí)最小只能1") @Max(value = 99, message = "班級(jí)最大只能99") @RequestParam(name = "classroom", required = true) int classroom) { System.out.println(grade + "," + classroom); } }
c.返回驗(yàn)證信息提示
可以看到:驗(yàn)證不通過時(shí),拋出了ConstraintViolationException異常,使用同一捕獲異常處理:
@ControllerAdvice @Component public class GlobalExceptionHandler { @ExceptionHandler @ResponseBody @ResponseStatus(HttpStatus.BAD_REQUEST) public String handle(ValidationException exception) { if(exception instanceof ConstraintViolationException){ ConstraintViolationException exs = (ConstraintViolationException) exception; Set<ConstraintViolation<?>> violations = exs.getConstraintViolations(); for (ConstraintViolation<?> item : violations) { /**打印驗(yàn)證不通過的信息*/ System.out.println(item.getMessage()); } } return "bad request, " ; } }
d.驗(yàn)證
瀏覽器服務(wù)請(qǐng)求地址:http://localhost:8080/validation/demo3?grade=18&classroom=888
沒有配置快速失敗返回的MethodValidationPostProcessor 時(shí)輸出信息如下:
年級(jí)只能從1-9
班級(jí)最大只能99
配置了快速失敗返回的MethodValidationPostProcessor 時(shí)輸出信息如下:
年級(jí)只能從1-9
瀏覽器服務(wù)請(qǐng)求地址:http://localhost:8080/validation/demo3?grade=0&classroom=0
沒有配置快速失敗返回的MethodValidationPostProcessor 時(shí)輸出信息如下:
年級(jí)只能從1-9
班級(jí)最小只能1
配置了快速失敗返回的MethodValidationPostProcessor 時(shí)輸出信息如下:
年級(jí)只能從1-9
3、model校驗(yàn)
待校驗(yàn)的model:
@Data public class Demo2 { @Length(min = 5, max = 17, message = "length長(zhǎng)度在[5,17]之間") private String length; /**@Size不能驗(yàn)證Integer,適用于String, Collection, Map and arrays*/ @Size(min = 1, max = 3, message = "size在[1,3]之間") private String age; @Range(min = 150, max = 250, message = "range在[150,250]之間") private int high; @Size(min = 3,max = 5,message = "list的Size在[3,5]") private List<String> list; }
驗(yàn)證model,以下全部驗(yàn)證通過:
@Autowired private Validator validator; @RequestMapping("/demo3") public void demo3(){ Demo2 demo2 = new Demo2(); demo2.setAge("111"); demo2.setHigh(150); demo2.setLength("ABCDE"); demo2.setList(new ArrayList<String>(){{add("111");add("222");add("333");}}); Set<ConstraintViolation<Demo2>> violationSet = validator.validate(demo2); for (ConstraintViolation<Demo2> model : violationSet) { System.out.println(model.getMessage()); } }
4、對(duì)象級(jí)聯(lián)校驗(yàn)
對(duì)象內(nèi)部包含另一個(gè)對(duì)象作為屬性,屬性上加@Valid,可以驗(yàn)證作為屬性的對(duì)象內(nèi)部的驗(yàn)證:(驗(yàn)證Demo2示例時(shí),可以驗(yàn)證Demo2的字段)
@Data public class Demo2 { @Size(min = 3,max = 5,message = "list的Size在[3,5]") private List<String> list; @NotNull @Valid private Demo3 demo3; } @Data public class Demo3 { @Length(min = 5, max = 17, message = "length長(zhǎng)度在[5,17]之間") private String extField; }
級(jí)聯(lián)校驗(yàn):
/**前面配置了快速失敗返回的Bean*/ @Autowired private Validator validator; @RequestMapping("/demo3") public void demo3(){ Demo2 demo2 = new Demo2(); demo2.setList(new ArrayList<String>(){{add("111");add("222");add("333");}}); Demo3 demo3 = new Demo3(); demo3.setExtField("22"); demo2.setDemo3(demo3); Set<ConstraintViolation<Demo2>> violationSet = validator.validate(demo2); for (ConstraintViolation<Demo2> model : violationSet) { System.out.println(model.getMessage()); } }
可以校驗(yàn)Demo3的extField字段。
5、分組校驗(yàn)
結(jié)論:分組順序校驗(yàn)時(shí),按指定的分組先后順序進(jìn)行驗(yàn)證,前面的驗(yàn)證不通過,后面的分組就不行驗(yàn)證。
有這樣一種場(chǎng)景,新增用戶信息的時(shí)候,不需要驗(yàn)證userId(因?yàn)橄到y(tǒng)生成);修改的時(shí)候需要驗(yàn)證userId,這時(shí)候可用用戶到validator的分組驗(yàn)證功能。
設(shè)置validator為普通驗(yàn)證模式("hibernate.validator.fail_fast", "false"),用到的驗(yàn)證GroupA、GroupB和model:
GroupA、GroupB: public interface GroupA { } public interface GroupB { }
驗(yàn)證model:Person
@Data public class Person { @NotBlank @Range(min = 1,max = Integer.MAX_VALUE,message = "必須大于0",groups = {GroupA.class}) /**用戶id*/ private Integer userId; @NotBlank @Length(min = 4,max = 20,message = "必須在[4,20]",groups = {GroupB.class}) /**用戶名*/ private String userName; @NotBlank @Range(min = 0,max = 100,message = "年齡必須在[0,100]",groups={Default.class}) /**年齡*/ private Integer age; @Range(min = 0,max = 2,message = "性別必須在[0,2]",groups = {GroupB.class}) /**性別 0:未知;1:男;2:女*/ private Integer sex; }
如上Person所示,3個(gè)分組分別驗(yàn)證字段如下:
a、分組
只驗(yàn)證GroupA、GroupB標(biāo)記的分組:
@RequestMapping("/demo5") public void demo5(){ Person p = new Person(); /**GroupA驗(yàn)證不通過*/ p.setUserId(-12); /**GroupA驗(yàn)證通過*/ //p.setUserId(12); p.setUserName("a"); p.setAge(110); p.setSex(5); Set<ConstraintViolation<Person>> validate = validator.validate(p, GroupA.class, GroupB.class); for (ConstraintViolation<Person> item : validate) { System.out.println(item); } }
或
@RequestMapping("/demo6") public void demo6(@Validated({GroupA.class, GroupB.class}) Person p, BindingResult result){ if(result.hasErrors()){ List<ObjectError> allErrors = result.getAllErrors(); for (ObjectError error : allErrors) { System.out.println(error); } } }
GroupA、GroupB、Default都驗(yàn)證不通過的情況:
驗(yàn)證信息如下所示:
ConstraintViolationImpl{interpolatedMessage='必須在[4,20]', propertyPath=userName, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必須在[4,20]'} ConstraintViolationImpl{interpolatedMessage='必須大于0', propertyPath=userId, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必須大于0'} ConstraintViolationImpl{interpolatedMessage='性別必須在[0,2]', propertyPath=sex, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='性別必須在[0,2]'}
GroupA驗(yàn)證通過、GroupB、Default驗(yàn)證不通過的情況:
驗(yàn)證信息如下所示:
ConstraintViolationImpl{interpolatedMessage='必須在[4,20]', propertyPath=userName, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必須在[4,20]'} ConstraintViolationImpl{interpolatedMessage='性別必須在[0,2]', propertyPath=sex, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='性別必須在[0,2]'}
b、組序列
除了按組指定是否驗(yàn)證之外,還可以指定組的驗(yàn)證順序,前面組驗(yàn)證不通過的,后面組不進(jìn)行驗(yàn)證:
指定組的序列(GroupA》GroupB》Default):
@GroupSequence({GroupA.class, GroupB.class, Default.class}) public interface GroupOrder { }
測(cè)試demo:
@RequestMapping("/demo7") public void demo7(){ Person p = new Person(); /**GroupA驗(yàn)證不通過*/ //p.setUserId(-12); /**GroupA驗(yàn)證通過*/ p.setUserId(12); p.setUserName("a"); p.setAge(110); p.setSex(5); Set<ConstraintViolation<Person>> validate = validator.validate(p, GroupOrder.class); for (ConstraintViolation<Person> item : validate) { System.out.println(item); } }
或
@RequestMapping("/demo8") public void demo8(@Validated({GroupOrder.class}) Person p, BindingResult result){ if(result.hasErrors()){ List<ObjectError> allErrors = result.getAllErrors(); for (ObjectError error : allErrors) { System.out.println(error); } } }
GroupA、GroupB、Default都驗(yàn)證不通過的情況:
驗(yàn)證信息如下所示:
ConstraintViolationImpl{interpolatedMessage='必須大于0', propertyPath=userId, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必須大于0'}
GroupA驗(yàn)證通過、GroupB、Default驗(yàn)證不通過的情況:
驗(yàn)證信息如下所示:
ConstraintViolationImpl{interpolatedMessage='必須在[4,20]', propertyPath=userName, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='必須在[4,20]'} ConstraintViolationImpl{interpolatedMessage='性別必須在[0,2]', propertyPath=sex, rootBeanClass=class validator.demo.project.model.Person, messageTemplate='性別必須在[0,2]'}
結(jié)論:分組順序校驗(yàn)時(shí),按指定的分組先后順序進(jìn)行驗(yàn)證,前面的驗(yàn)證不通過,后面的分組就不行驗(yàn)證。
五、自定義驗(yàn)證器
一般情況,自定義驗(yàn)證可以解決很多問題。但也有無法滿足情況的時(shí)候,此時(shí),我們可以實(shí)現(xiàn)validator的接口,自定義自己需要的驗(yàn)證器。
如下所示,實(shí)現(xiàn)了一個(gè)自定義的大小寫驗(yàn)證器:
public enum CaseMode { UPPER, LOWER; } @Target( { ElementType.METHOD, ElementType.FIELD, ElementType.ANNOTATION_TYPE }) @Retention(RetentionPolicy.RUNTIME) @Constraint(validatedBy = CheckCaseValidator.class) @Documented public @interface CheckCase { String message() default ""; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; CaseMode value(); } public class CheckCaseValidator implements ConstraintValidator<CheckCase, String> { private CaseMode caseMode; public void initialize(CheckCase checkCase) { this.caseMode = checkCase.value(); } public boolean isValid(String s, ConstraintValidatorContext constraintValidatorContext) { if (s == null) { return true; } if (caseMode == CaseMode.UPPER) { return s.equals(s.toUpperCase()); } else { return s.equals(s.toLowerCase()); } } }
要驗(yàn)證的Model:
public class Demo{ @CheckCase(value = CaseMode.LOWER,message = "userName必須是小寫") private String userName; public String getUserName() { return userName; } public void setUserName(String userName) { this.userName = userName; } }
validator配置:
@Bean public Validator validator(){ ValidatorFactory validatorFactory = Validation.byProvider( HibernateValidator.class ) .configure() .addProperty( "hibernate.validator.fail_fast", "true" ) .buildValidatorFactory(); Validator validator = validatorFactory.getValidator(); return validator; }
驗(yàn)證測(cè)試:
@RequestMapping("/demo4") public void demo4(){ Demo demo = new Demo(); demo.setUserName("userName"); Set<ConstraintViolation<Demo>> validate = validator.validate(demo); for (ConstraintViolation<Demo> dem : validate) { System.out.println(dem.getMessage()); } }
輸出結(jié)果:
userName必須是小寫
六、常見的注解
Bean Validation 中內(nèi)置的 constraint @Null 被注釋的元素必須為 null @NotNull 被注釋的元素必須不為 null @AssertTrue 被注釋的元素必須為 true @AssertFalse 被注釋的元素必須為 false @Min(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值 @Max(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值 @DecimalMin(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須大于等于指定的最小值 @DecimalMax(value) 被注釋的元素必須是一個(gè)數(shù)字,其值必須小于等于指定的最大值 @Size(max=, min=) 被注釋的元素的大小必須在指定的范圍內(nèi) @Digits (integer, fraction) 被注釋的元素必須是一個(gè)數(shù)字,其值必須在可接受的范圍內(nèi) @Past 被注釋的元素必須是一個(gè)過去的日期 @Future 被注釋的元素必須是一個(gè)將來的日期 @Pattern(regex=,flag=) 被注釋的元素必須符合指定的正則表達(dá)式 Hibernate Validator 附加的 constraint @NotBlank(message =) 驗(yàn)證字符串非null,且長(zhǎng)度必須大于0 @Email 被注釋的元素必須是電子郵箱地址 @Length(min=,max=) 被注釋的字符串的大小必須在指定的范圍內(nèi) @NotEmpty 被注釋的字符串的必須非空 @Range(min=,max=,message=) 被注釋的元素必須在合適的范圍內(nèi) //大于0.01,不包含0.01 @NotNull @DecimalMin(value = "0.01", inclusive = false) private Integer greaterThan; //大于等于0.01 @NotNull @DecimalMin(value = "0.01", inclusive = true) private BigDecimal greatOrEqualThan; @Length(min = 1, max = 20, message = "message不能為空") //不能將Length錯(cuò)用成Range //@Range(min = 1, max = 20, message = "message不能為空") private String message;
七、參考資料
參考資料:
http://docs.jboss.org/hibernate/validator/4.2/reference/zh-CN/html_single/#validator-gettingstarted
本文名稱:springboot使用hibernatevalidator校驗(yàn)方式
網(wǎng)站地址:http://jinyejixie.com/article4/jojjoe.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站策劃、營(yíng)銷型網(wǎng)站建設(shè)、App設(shè)計(jì)、響應(yīng)式網(wǎng)站、網(wǎng)站設(shè)計(jì)公司、手機(jī)網(wǎng)站建設(shè)
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請(qǐng)盡快告知,我們將會(huì)在第一時(shí)間刪除。文章觀點(diǎn)不代表本網(wǎng)站立場(chǎng),如需處理請(qǐng)聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時(shí)需注明來源: 創(chuàng)新互聯(lián)