關于SpringBoot中controller參數(shù)校驗的使用
參數(shù)校驗主要使用兩個標簽@Validated和@Valid;
@Valid是Hibernate的注解校驗,@Validated是spring的,是@Valid的增強;這兩個標簽也有一些不同之處,@Valid可以標注在成員屬性上也可以嵌套校驗,而@Validated不行,但是@Validated可以使用分組校驗;
maven導入:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.7.5</version> </dependency>
通常用到的注解基本都在javax.validation.constraints包下,基本都有value(設定值)、message(設置錯誤消息)、groups(指定分組)屬性:
- @AssertFalse:必須為false,支持boolean和Boolean,null是有效的;
- @AssertTrue:必須為true,支持boolean和Boolean,null是有效的;
- @DecimalMax:必須是一個小于或者小于等于設定值的數(shù)字,可以用inclusive指定是否包含數(shù)字(默認true),并且value是String類型的,支持BigDecimal、BigInteger、CharSequence、byte / short / int / long以及它們的包裝類(由于舍入原因不支持double和float),null是有效的;
- @DecimalMin:必須是一個大于或者大于等于設定值的數(shù)字,其他同@DecimalMax;
- @Digits:設定可接受范圍內(nèi)的數(shù)字,必須指定integer(整數(shù)位數(shù))和fraction(小數(shù)位數(shù)),支持BigDecimal、BigInteger、CharSequence、byte / short / int / long以及它們的包裝類,null是有效的;
- @Email:必須是一個正確格式的郵箱,可以使用regexp指定正則表達式(默認是任意字符串),可以使用flags指定正則表達式的選項,null是有效的;
- @Future:必須是一個未來的瞬間、日期或時間(Now是虛擬機默認的當前時區(qū)的時間),支持java.util.Date、java.util.Calendar、java.time.Instant、java.time.LocalDate、java.time.LocalDateTime、java.time.LocalTime、java.time.MonthDay、java.time.OffsetDateTime、java.time.OffsetTime、java.time.Year、java.time.YearMonth、java.time.ZonedDateTime、java.time.chrono.HijrahDate、java.time.chrono.JapaneseDate、java.time.chrono.MinguoDate、java.time.chrono.ThaiBuddhistDate,null是有效的;
- @FutureOrPresent:必須是現(xiàn)在或未來的瞬間、日期或時間,其他同@Future;
- @Max:必須是小于等于指定值的數(shù)字,value是long類型,支持BigDecimal、BigInteger、byte / short / int / long以及它們的包裝類(不支持float和double),null是有效的;
- @Min:必須是大于等于指定值的數(shù)字,其他同@Max;
- @Negative:必須是一個嚴格的負數(shù),0為無效值,支持BigDecimal、BigInteger、byte / short / int / long / float / double以及它們的包裝類,null是有效的;
- @NegativeOrZero,必須是負數(shù)或者0,其他同@Negative;
- @NotBlank:不能為null,并且至少包含一個非空白字符,接受CharSequence;
- @NotEmpty:不能為null或空(集合),支持CharSequence(字符序列長度)、Collection(集合size)、Map(map size)、Array(數(shù)組長度);
- @NotNull:不能為null,支持所有類型;
- @Null:必須為null,支持所有類型;
- @Past:必須是一個過去的瞬間、日期或時間,其他同@Future;
- @PastOrPresent:必須是現(xiàn)在或過去的瞬間、日期或時間,其他同@Future;
- @Pattern:必須符合指定的正則表達式,必須使用regexp參數(shù)指定正則表達式;
- @Positive:必須是一個嚴格的正數(shù),0為無效值,其他同@Negative;
- @PositiveOrZero:必須是正數(shù)或者0,其他同@Negative;
- @Size:指定元素大小必須在指定范圍內(nèi)(包括邊界值),使用min指定下邊界(默認0),使用max指定上邊界(默認Integer.MAX_VALUE),支持CharSequence、Collection、Map、Array,null是有效的;
單參數(shù)校驗
在controller類上添加@Validated標簽,在方法的參數(shù)前加驗證標簽,并且同一個參數(shù)可以添加多個標簽;
啟動類:(使用默認配置,端口8080)
/** * 2022年12月2日下午4:00:48 */ package testspringboot.test6paramvalidation; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; /** * @author XWF * */ @SpringBootApplication public class Test6Main { /** * @param args */ public static void main(String[] args) { SpringApplication.run(Test6Main.class, args); } }
controller類:
/** * 2022年12月2日下午4:05:34 */ package testspringboot.test6paramvalidation; import java.util.List; import java.util.stream.Collectors; import javax.validation.constraints.Max; import javax.validation.constraints.Min; import javax.validation.constraints.NotNull; import org.springframework.validation.BindingResult; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; /** * @author XWF * */ @RestController @RequestMapping("/test6") @Validated public class Test6Controller { @RequestMapping("/a") public String a(@NotNull(message = "參數(shù)s不能為null") String s, @Min(5) @Max(value = 10) long a) { System.out.println(s); System.out.println(a); return String.format("s:%s a:%d", s, a); } }
postman測試:
返回的錯誤msg也可以使用自定義設定,使用@RestControllerAdvice注釋一個類,然后在類里定義各種錯誤msg,就像這樣:
package testspringboot.test6paramvalidation; import java.util.List; import java.util.stream.Collectors; import javax.validation.ConstraintViolation; import javax.validation.ConstraintViolationException; import org.springframework.validation.ObjectError; import org.springframework.web.bind.MethodArgumentNotValidException; import org.springframework.web.bind.annotation.ExceptionHandler; import org.springframework.web.bind.annotation.RestControllerAdvice; @RestControllerAdvice public class ValidException { @ExceptionHandler(value = MethodArgumentNotValidException.class) public String handleValidException(MethodArgumentNotValidException e) { List<String> msgList = e.getBindingResult().getAllErrors() .stream() .map(ObjectError::getDefaultMessage) .collect(Collectors.toList()); return "MethodArgumentNotValidException: " + msgList.toString(); } @ExceptionHandler(value = ConstraintViolationException.class) public String handleConstraintViolationException(ConstraintViolationException e) { List<String> msgList = e.getConstraintViolations() .stream() .map(ConstraintViolation::getMessage) .collect(Collectors.toList()); return "ConstraintViolationException: " + msgList.toString();//返回錯誤描述 } }
再次測試結果:
默認驗證所有參數(shù),即使前面驗證不通過也會繼續(xù)驗證,可以設置快速失敗,使驗證失敗立即返回不繼續(xù)驗證;
自定義注入Validator類:
package testspringboot.test6paramvalidation; import javax.validation.Validation; import javax.validation.Validator; import javax.validation.ValidatorFactory; import org.hibernate.validator.HibernateValidator; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; @Configuration public class ValidatConfig { @Bean public Validator validator() { ValidatorFactory vfactory = Validation.byProvider(HibernateValidator.class) .configure() .failFast(true)//開啟快速失敗 .buildValidatorFactory(); return vfactory.getValidator(); } }
再次測試結果,只顯示一個錯誤msg了:
實體類校驗
只需要在controller類的方法實體類參數(shù)前加@Validated或者@Valid標簽,實體類里的屬性前加驗證標簽,controller類上可以不用@Validated標簽也行;
實體類:
package testspringboot.test6paramvalidation; import javax.validation.constraints.Max; import javax.validation.constraints.NotNull; public class Bparam { @NotNull public String s; @Max(value = 10, message = "Bparam的x參數(shù)不能超過10") public int x; public String getS() { return s; } public void setS(String s) { this.s = s; } public int getX() { return x; } public void setX(int x) { this.x = x; } @Override public String toString() { return "Bparam [s=" + s + ", x=" + x + "]"; } }
controller類里的方法:
@RequestMapping("/b") public String b(@Valid Bparam b) { return b.toString(); }
測試:
另外錯誤消息也可以在controller類的方法參數(shù)里接收,參數(shù)里使用BindingResult就可以處理:
@RequestMapping("/b") public String b(@Valid Bparam b, BindingResult result) { if (result.hasErrors()) { List<String> errors = result.getAllErrors().stream().map(x -> x.getDefaultMessage()).collect(Collectors.toList()); return "BindingResult Errors: " + errors.toString(); } return b.toString(); }
使用post的消息體接收參數(shù)也一樣,在參數(shù)前多加一個@RequestBody:
@RequestMapping("/b") public String b(@RequestBody @Validated Bparam b, BindingResult result) { if (result.hasErrors()) { List<String> errors = result.getAllErrors().stream().map(x -> x.getDefaultMessage()).collect(Collectors.toList()); return "BindingResult Errors: " + errors.toString(); } return b.toString(); }
分組校驗
可以為同一屬性設置不同情況下應用不同的注解標簽,需要在注解標簽里使用groups參數(shù),groups是一個class集合,一個標簽可以設置多個group,在controller類里方法的參數(shù)前的@Validated標簽里使用value指定要使用的group驗證(可以指定多個group驗證),沒有設置groups的標簽默認屬于Default.class的group,設置group的class通常使用interface,可以寫在外面或者直接寫到實體類內(nèi)部;
實體類:
/** * 2023年1月13日上午11:08:47 */ package testspringboot.test6paramvalidation; import javax.validation.constraints.AssertFalse; import javax.validation.constraints.AssertTrue; import javax.validation.constraints.Min; import javax.validation.constraints.NotBlank; import javax.validation.constraints.Size; /** * @author XWF * */ public class Cparam { @AssertTrue(message = "b應為true", groups = CparamBTrue.class) @AssertFalse(message = "b應為false", groups = CparamBFalse.class) public boolean b; @NotBlank @Size(min = 1, max = 5, message = "s的長度1~5") public String s; @Min(20) public int i; // interface CparamBTrue{} // interface CparamBFalse{} } interface CparamBTrue{} interface CparamBFalse{}
controller方法:
@RequestMapping("/c1") public String c1(@RequestBody @Validated(CparamBTrue.class) Cparam c, BindingResult result) { if (result.hasErrors()) { List<String> errors = result.getAllErrors().stream().map(x -> x.getDefaultMessage()).collect(Collectors.toList()); return "BindingResult Errors: " + errors.toString(); } return "OK"; } @RequestMapping("/c2") public String c2(@RequestBody @Validated(value = {CparamBFalse.class, Default.class}) Cparam c, BindingResult result) { if (result.hasErrors()) { List<String> errors = result.getAllErrors().stream().map(x -> x.getDefaultMessage()).collect(Collectors.toList()); return "BindingResult Errors: " + errors.toString(); } return "OK"; }
分組測試:
c1只驗證了group是CparamBTrue的成員b,c2除了驗證了group是CparamBFalse的成員b,也驗證了沒有設置groups的s和i;
另外也可以設置動態(tài)組校驗,根據(jù)某些條件和情況設置驗證的groups,需要在實體類上添加@GroupSequenceProvider標簽指定實現(xiàn)了DefaultGroupSequenceProvider接口并實現(xiàn)接口里getValidationGroups方法的class,getValidationGroups方法返回List<Class<?>>,即為當前請求需要使用的groups(返回值相當于controller類方法參數(shù)前@Validated標簽里的value的作用);
例如根據(jù)實體類內(nèi)boolean值指定int值使用正負數(shù):
實體類:
/** * 2023年1月13日下午3:03:56 */ package testspringboot.test6paramvalidation; import javax.validation.constraints.Negative; import javax.validation.constraints.NotNull; import javax.validation.constraints.Positive; import org.hibernate.validator.group.GroupSequenceProvider; /** * @author XWF * */ @GroupSequenceProvider(value = C3paramGroupProvider.class) public class C3param { @NotNull(message = "b不能為null") public boolean b; @NotNull @Positive(message = "b為true時i應大于0", groups = BTrue.class) @Negative(message = "b為false時i應小于0", groups = BFalse.class) public int i; @Override public String toString() { return "C3param [b=" + b + ", i=" + i + "]"; } interface BTrue{} interface BFalse{} }
GroupProvider:
/** * 2023年1月13日下午3:10:32 */ package testspringboot.test6paramvalidation; import java.util.ArrayList; import java.util.List; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; /** * @author XWF * */ public class C3paramGroupProvider implements DefaultGroupSequenceProvider<C3param> { @Override public List<Class<?>> getValidationGroups(C3param object) { System.out.println("obj:" + object); List<Class<?>> groupList = new ArrayList<>(); groupList.add(C3param.class);//實體類需要加入 if (object != null) {//該方法會調(diào)用多次,object可能為null //b為true時使用BTrue.class組,b為false時使用BFalse.class組 groupList.add(object.b ? C3param.BTrue.class : C3param.BFalse.class); } return groupList; } }
controller方法:
@RequestMapping("/c3") public String c3(@RequestBody @Validated C3param c3, BindingResult result) { System.out.println("param:" + c3); if (result.hasErrors()) { List<String> errors = result.getAllErrors().stream().map(x -> x.getDefaultMessage()).collect(Collectors.toList()); return "BindingResult Errors: " + errors.toString(); } return "OK"; }
測試:
嵌套校驗
實體類成員為另一個級聯(lián)的類時,需要在成員前使用@Valid標簽(支持嵌套),并且提供該級聯(lián)的成員屬性的get方法,另外級聯(lián)類內(nèi)部也需要提供需要驗證的成員屬性的get方法(不驗證的成員不用get方法)
第一層實體類:
/** * 2023年1月13日下午3:27:12 */ package testspringboot.test6paramvalidation; import javax.validation.Valid; import javax.validation.constraints.AssertTrue; /** * @author XWF * */ public class D1param { @AssertTrue(message = "D1param.b必須為true") public boolean b; @Valid public D2param d2; public D2param getD2() {//級聯(lián)對象需要get方法 return d2; } }
第二層實體類:
/** * 2023年1月13日下午3:27:28 */ package testspringboot.test6paramvalidation; import javax.validation.Valid; import javax.validation.constraints.Positive; /** * @author XWF * */ public class D2param { @Positive(message = "D2param.i必須為正數(shù)") public int i; public String s;//不驗證,不需get方法 @Valid public D3param d3; public int getI() {//成員需要get方法 return i; } public D3param getD3() {//級聯(lián)對象需要get方法 return d3; } }
第三層實體類:
/** * 2023年1月13日下午3:37:33 */ package testspringboot.test6paramvalidation; import javax.validation.constraints.NotNull; /** * @author XWF * */ public class D3param { @NotNull(message = "D3param.s不能為null") public String s; public String getS() {//成員需要get方法 return s; } }
controller方法:
@RequestMapping("/d") public String d(@RequestBody @Validated D1param d1, BindingResult result) { if (result.hasErrors()) { List<String> errors = result.getAllErrors().stream().map(x -> x.getDefaultMessage()).collect(Collectors.toList()); return "BindingResult Errors: " + errors.toString(); } return "OK"; }
測試:
自定義注解
定義一個注解,使用@Retention、@Target、@Constraint標簽注釋,并攜帶三個方法message()、groups()、payload(),并在@Constraint標簽里使用validatedBy屬性指定自定義驗證類,自定義驗證類實現(xiàn)ConstraintValidator<A extends Annotation, T>接口的boolean isValid(T value, ConstraintValidatorContext context)方法,判斷驗證是否通過;
自定義注解:(功能:驗證是偶數(shù))
/** * 2023年1月13日下午4:24:06 */ package testspringboot.test6paramvalidation; import static java.lang.annotation.ElementType.FIELD; import static java.lang.annotation.RetentionPolicy.RUNTIME; import java.lang.annotation.Retention; import java.lang.annotation.Target; import javax.validation.Constraint; import javax.validation.Payload; @Retention(RUNTIME) @Target(FIELD) @Constraint(validatedBy = EValidator.class) /** * @author XWF * */ public @interface EAnnotation { String message() default "應該是偶數(shù)"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
對應的驗證類:
/** * 2023年1月13日下午4:25:39 */ package testspringboot.test6paramvalidation; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; /** * @author XWF * */ public class EValidator implements ConstraintValidator<EAnnotation, Integer> { @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { return value % 2 == 0; } }
實體類:
/** * 2023年1月13日下午3:52:20 */ package testspringboot.test6paramvalidation; /** * @author XWF * */ public class Eparam { @EAnnotation public int i; }
controller方法:
@RequestMapping("/e") public String e(@RequestBody @Validated Eparam e, BindingResult result) { if (result.hasErrors()) { List<String> errors = result.getAllErrors().stream().map(x -> x.getDefaultMessage()).collect(Collectors.toList()); return "BindingResult Errors: " + errors.toString(); } return "OK"; }
測試:
到此這篇關于關于SpringBoot中controller參數(shù)校驗的使用的文章就介紹到這了,更多相關SpringBoot controller參數(shù)校驗內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
Java的微信開發(fā)中使用XML格式和JSON格式數(shù)據(jù)的示例
這篇文章主要介紹了Java微信開發(fā)中使用XML格式和JSON格式數(shù)據(jù)的示例,注意一下json-lib所需要的jar包,需要的朋友可以參考下2016-02-02java中實現(xiàn)list或set轉(zhuǎn)map的方法
這篇文章主要介紹了java中實現(xiàn)list或set轉(zhuǎn)map的方法的相關資料,需要的朋友可以參考下2017-01-01SpringCloud使用Nacos保存和讀取變量的配置方法
在使用SpringCloud開發(fā)微服務時,經(jīng)常會遇到一些比較小的后臺參數(shù)配置,這些配置不足以單獨開一張表去存儲,而且其他服務會讀取該參數(shù),這篇文章主要介紹了SpringCloud使用Nacos保存和讀取變量,需要的朋友可以參考下2022-07-07使用cmd根據(jù)WSDL網(wǎng)址生成java客戶端代碼的實現(xiàn)
這篇文章主要介紹了使用cmd根據(jù)WSDL網(wǎng)址生成java客戶端代碼的實現(xiàn)方式,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03注解@TableName,@TableField,pgsql的模式對應方式
這篇文章主要介紹了注解@TableName,@TableField,pgsql的模式對應方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教2024-04-04