title: SpringBoot校驗(validation)
date: 2019-07-15
author: maxzhao
tags:
- SpringBoot
- validation
- hibernate-validation
categories:
- SpringBoot
元編程
一個健壯的系統(tǒng)都要對外部提交的數(shù)據(jù)進(jìn)行完整性、合法性的校驗弥喉。
校驗是我們程序開發(fā)中必不可少的過程郁竟。
即使開發(fā)一個不面對最終用戶的工具包,也需要對傳入的數(shù)據(jù)進(jìn)行縝密的校驗來防止引發(fā)底層難以追蹤的問題由境。
后端參數(shù)校驗最簡單的做法是直接在業(yè)務(wù)方法里面進(jìn)行判斷棚亩,當(dāng)判斷成功之后再繼續(xù)往下執(zhí)行。但這樣帶給我們的是代碼的耦合虏杰,冗余讥蟆。當(dāng)我們多個地方需要校驗時,我們就需要在每一個地方調(diào)用校驗程序,導(dǎo)致代碼很冗余纺阔,且不美觀瘸彤。
不使用Bean Validation校驗數(shù)據(jù)的代碼基本都是靠大量的 if-else
.所以我這里學(xué)習(xí)使用了 注解方式實現(xiàn)數(shù)據(jù)校驗.
使用
SpringBoot 中的 bean validation
是集成了hibernate-validator
和tomcat-embed-el
引入依賴
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
簡單的校驗
@Valid:常見用在方法,類中字段上進(jìn)行校驗
@Validated:是spring提供的對@Valid的封裝笛钝,常見用在方法上進(jìn)行校驗
BindingResult:是驗證是否錯誤
Model中
@Range(max = 150, min = 1, message = "年齡范圍應(yīng)該在1-150內(nèi)质况。")
private Integer age;
Controller中
@PostMapping("save")
public void v1(@RequestBody @Valid AppUser appUser,BindingResult result){
if(result.hasErrors()){
for (ObjectError error : result.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
綁定多個校驗對象
@PostMapping("save")
public void v1(@RequestBody @Valid AppUser appUser,BindingResult result,@RequestBody @Valid AppUser appUser2,BindingResult result2){
if(result.hasErrors()){
for (ObjectError error : result.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
部分標(biāo)簽
Bean Validation 中內(nèi)置的 constraint
注解 | 作用 |
---|---|
@Valid | 被注釋的元素是一個對象,需要檢查此對象的所有字段值 |
@Null | 被注釋的元素必須為 null |
@NotNull | 被注釋的元素必須不為 null |
@AssertTrue | 被注釋的元素必須為 true |
@AssertFalse | 被注釋的元素必須為 false |
@Min(value) | 被注釋的元素必須是一個數(shù)字婆翔,其值必須大于等于指定的最小值 |
@Max(value) | 被注釋的元素必須是一個數(shù)字拯杠,其值必須小于等于指定的最大值 |
@DecimalMin(value) | 被注釋的元素必須是一個數(shù)字,其值必須大于等于指定的最小值 |
@DecimalMax(value) | 被注釋的元素必須是一個數(shù)字啃奴,其值必須小于等于指定的最大值 |
@Size(max, min) | 被注釋的元素的大小必須在指定的范圍內(nèi) |
@Digits (integer, fraction) | 被注釋的元素必須是一個數(shù)字潭陪,其值必須在可接受的范圍內(nèi) |
@Past | 被注釋的元素必須是一個過去的日期 |
@Future | 被注釋的元素必須是一個將來的日期 |
@Pattern(value) | 被注釋的元素必須符合指定的正則表達(dá)式 |
Hibernate Validator 附加的 constraint
注解 | 作用 |
---|---|
被注釋的元素必須是電子郵箱地址 | |
@Length(min=, max=) | 被注釋的字符串的大小必須在指定的范圍內(nèi) |
@NotEmpty | 被注釋的字符串的必須非空 |
@Range(min=, max=) | 被注釋的元素必須在合適的范圍內(nèi) |
@NotBlank | 被注釋的字符串的必須非空 |
@URL(protocol=,host=, port=,regexp=, flags=) | 被注釋的字符串必須是一個有效的url |
容易記錯的
@NotNull 任何對象的value不能為null
@NotEmpty 集合對象的元素不為0,即集合不為空最蕾,也可以用于字符串不為null
@NotBlank 只能用于字符串不為null依溯,并且字符串trim()以后length要大于0
resource 下新建錯誤信息配置文件
在resource 目錄下新建提示信息配置文件 ValidationMessages.properties
文件中的格式為message=ASCII
.
中文的ascii碼
hibernate的校驗?zāi)J?/h2>
1、普通模式(默認(rèn)是這個模式)
普通模式(會校驗完所有的屬性瘟则,然后返回所有的驗證失敗信息)
2黎炉、快速失敗返回模式
快速失敗返回模式(只要有一個驗證失敗,則返回)
兩種驗證模式配置方式
(參考官方文檔)
failFast:true 快速失敗返回模式 false 普通模式
@Configuration public class ValidatorConfiguration { @Bean public Validator validator(){ ValidatorFactory validatorFactory = Validation .byProvider( HibernateValidator.class ) .configure() .failFast( true ) .buildValidatorFactory(); Validator validator = validatorFactory.getValidator(); } }
和 (hibernate.validator.fail_fast:true 快速失敗返回模式 false 普通模式)
@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; } }
手動驗證Model
AppUser appUser=new AppUser();
Set<ConstraintViolation<AppUser>> violationSet = validator.validate(appUser);
for (ConstraintViolation<AppUser> model : violationSet) {
System.out.println(model.getMessage());
}
分組校驗(區(qū)分新增和修改的規(guī)則)
簡單使用
場景
新增用戶信息和修改用戶信息所需要驗證的字段是不同的.
public interface AppUserVaildC {
}
public interface AppUserVaildU {
}
Model中
Default 是默認(rèn)分組.
@Range(min = 0,max = 100,message = "年齡必須在[0,100]",groups={Default.class})
/**年齡*/
private Integer age;
@Range(min = 0,max = 2,message = "性別必須在[0,2]",groups = {AppUserVaildC.class})
/**性別 0:未知醋拧;1:男慷嗜;2:女*/
private Integer sex;
Controller中使用
@PostMapping("save")
public void v1(@RequestBody @Validated({AppUserVaildC.class, AppUserVaildU.class}) AppUser appUser,BindingResult result){
if(result.hasErrors()){
for (ObjectError error : result.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
普通使用
AppUser appUser=new AppUser();
Set<ConstraintViolation<AppUser>> violationSet = validator.validate(appUser,AppUserVaildC,AppUserVaildU);
for (ConstraintViolation<AppUser> model : violationSet) {
System.out.println(model.getMessage());
}
組序列
除了按組指定是否驗證之外,還可以指定組的驗證順序丹壕,前面組驗證不通過的庆械,后面組不進(jìn)行驗證
@GroupSequence({AppUserVaildC.class, AppUserVaildU.class, Default.class})
public interface GroupOrder {
}
Controller中使用
@PostMapping("save")
public void v1(@RequestBody @Validated({GroupOrder.class}) AppUser appUser,BindingResult result){
if(result.hasErrors()){
for (ObjectError error : result.getAllErrors()) {
System.out.println(error.getDefaultMessage());
}
}
}
普通使用
AppUser appUser=new AppUser();
Set<ConstraintViolation<AppUser>> violationSet = validator.validate(appUser,GroupOrder);
for (ConstraintViolation<AppUser> model : violationSet) {
System.out.println(model.getMessage());
}
自定義驗證
下面是一個自定義大小寫的驗證
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());
}
}
}
Model中
@Range(value = CaseMode.LOWER ,message = "年必須是小寫",groups={Default.class})
/**年齡*/
private String loginName;
@Range(min = 0,max = 2,message = "性別必須在[0,2]",groups = {AppUserVaildC.class})
/**性別 0:未知;1:男菌赖;2:女*/
private Integer sex;
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;
}
本文地址: