關(guān)于SpringBoot中controller參數(shù)校驗(yàn)的使用
參數(shù)校驗(yàn)主要使用兩個(gè)標(biāo)簽@Validated和@Valid;
@Valid是Hibernate的注解校驗(yàn),@Validated是spring的,是@Valid的增強(qiáng);這兩個(gè)標(biāo)簽也有一些不同之處,@Valid可以標(biāo)注在成員屬性上也可以嵌套校驗(yàn),而@Validated不行,但是@Validated可以使用分組校驗(yàn);
maven導(dǎo)入:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-validation</artifactId> <version>2.7.5</version> </dependency>
通常用到的注解基本都在javax.validation.constraints包下,基本都有value(設(shè)定值)、message(設(shè)置錯(cuò)誤消息)、groups(指定分組)屬性:
- @AssertFalse:必須為false,支持boolean和Boolean,null是有效的;
- @AssertTrue:必須為true,支持boolean和Boolean,null是有效的;
- @DecimalMax:必須是一個(gè)小于或者小于等于設(shè)定值的數(shù)字,可以用inclusive指定是否包含數(shù)字(默認(rèn)true),并且value是String類型的,支持BigDecimal、BigInteger、CharSequence、byte / short / int / long以及它們的包裝類(由于舍入原因不支持double和float),null是有效的;
- @DecimalMin:必須是一個(gè)大于或者大于等于設(shè)定值的數(shù)字,其他同@DecimalMax;
- @Digits:設(shè)定可接受范圍內(nèi)的數(shù)字,必須指定integer(整數(shù)位數(shù))和fraction(小數(shù)位數(shù)),支持BigDecimal、BigInteger、CharSequence、byte / short / int / long以及它們的包裝類,null是有效的;
- @Email:必須是一個(gè)正確格式的郵箱,可以使用regexp指定正則表達(dá)式(默認(rèn)是任意字符串),可以使用flags指定正則表達(dá)式的選項(xiàng),null是有效的;
- @Future:必須是一個(gè)未來的瞬間、日期或時(shí)間(Now是虛擬機(jī)默認(rèn)的當(dāng)前時(shí)區(qū)的時(shí)間),支持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)在或未來的瞬間、日期或時(shí)間,其他同@Future;
- @Max:必須是小于等于指定值的數(shù)字,value是long類型,支持BigDecimal、BigInteger、byte / short / int / long以及它們的包裝類(不支持float和double),null是有效的;
- @Min:必須是大于等于指定值的數(shù)字,其他同@Max;
- @Negative:必須是一個(gè)嚴(yán)格的負(fù)數(shù),0為無效值,支持BigDecimal、BigInteger、byte / short / int / long / float / double以及它們的包裝類,null是有效的;
- @NegativeOrZero,必須是負(fù)數(shù)或者0,其他同@Negative;
- @NotBlank:不能為null,并且至少包含一個(gè)非空白字符,接受CharSequence;
- @NotEmpty:不能為null或空(集合),支持CharSequence(字符序列長度)、Collection(集合size)、Map(map size)、Array(數(shù)組長度);
- @NotNull:不能為null,支持所有類型;
- @Null:必須為null,支持所有類型;
- @Past:必須是一個(gè)過去的瞬間、日期或時(shí)間,其他同@Future;
- @PastOrPresent:必須是現(xiàn)在或過去的瞬間、日期或時(shí)間,其他同@Future;
- @Pattern:必須符合指定的正則表達(dá)式,必須使用regexp參數(shù)指定正則表達(dá)式;
- @Positive:必須是一個(gè)嚴(yán)格的正數(shù),0為無效值,其他同@Negative;
- @PositiveOrZero:必須是正數(shù)或者0,其他同@Negative;
- @Size:指定元素大小必須在指定范圍內(nèi)(包括邊界值),使用min指定下邊界(默認(rèn)0),使用max指定上邊界(默認(rèn)Integer.MAX_VALUE),支持CharSequence、Collection、Map、Array,null是有效的;
單參數(shù)校驗(yàn)
在controller類上添加@Validated標(biāo)簽,在方法的參數(shù)前加驗(yàn)證標(biāo)簽,并且同一個(gè)參數(shù)可以添加多個(gè)標(biāo)簽;
啟動(dòng)類:(使用默認(rèn)配置,端口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測試:

返回的錯(cuò)誤msg也可以使用自定義設(shè)定,使用@RestControllerAdvice注釋一個(gè)類,然后在類里定義各種錯(cuò)誤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();//返回錯(cuò)誤描述
}
}再次測試結(jié)果:

默認(rèn)驗(yàn)證所有參數(shù),即使前面驗(yàn)證不通過也會(huì)繼續(xù)驗(yàn)證,可以設(shè)置快速失敗,使驗(yàn)證失敗立即返回不繼續(xù)驗(yàn)證;
自定義注入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();
}
}再次測試結(jié)果,只顯示一個(gè)錯(cuò)誤msg了:

實(shí)體類校驗(yàn)
只需要在controller類的方法實(shí)體類參數(shù)前加@Validated或者@Valid標(biāo)簽,實(shí)體類里的屬性前加驗(yàn)證標(biāo)簽,controller類上可以不用@Validated標(biāo)簽也行;
實(shí)體類:
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();
}測試:

另外錯(cuò)誤消息也可以在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ù)前多加一個(gè)@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();
}
分組校驗(yàn)
可以為同一屬性設(shè)置不同情況下應(yīng)用不同的注解標(biāo)簽,需要在注解標(biāo)簽里使用groups參數(shù),groups是一個(gè)class集合,一個(gè)標(biāo)簽可以設(shè)置多個(gè)group,在controller類里方法的參數(shù)前的@Validated標(biāo)簽里使用value指定要使用的group驗(yàn)證(可以指定多個(gè)group驗(yàn)證),沒有設(shè)置groups的標(biāo)簽?zāi)J(rèn)屬于Default.class的group,設(shè)置group的class通常使用interface,可以寫在外面或者直接寫到實(shí)體類內(nèi)部;
實(shí)體類:
/**
* 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應(yīng)為true", groups = CparamBTrue.class)
@AssertFalse(message = "b應(yīng)為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只驗(yàn)證了group是CparamBTrue的成員b,c2除了驗(yàn)證了group是CparamBFalse的成員b,也驗(yàn)證了沒有設(shè)置groups的s和i;
另外也可以設(shè)置動(dòng)態(tài)組校驗(yàn),根據(jù)某些條件和情況設(shè)置驗(yàn)證的groups,需要在實(shí)體類上添加@GroupSequenceProvider標(biāo)簽指定實(shí)現(xiàn)了DefaultGroupSequenceProvider接口并實(shí)現(xiàn)接口里getValidationGroups方法的class,getValidationGroups方法返回List<Class<?>>,即為當(dāng)前請求需要使用的groups(返回值相當(dāng)于controller類方法參數(shù)前@Validated標(biāo)簽里的value的作用);
例如根據(jù)實(shí)體類內(nèi)boolean值指定int值使用正負(fù)數(shù):
實(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時(shí)i應(yīng)大于0", groups = BTrue.class)
@Negative(message = "b為false時(shí)i應(yīng)小于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);//實(shí)體類需要加入
if (object != null) {//該方法會(huì)調(diào)用多次,object可能為null
//b為true時(shí)使用BTrue.class組,b為false時(shí)使用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";
}測試:




嵌套校驗(yàn)
實(shí)體類成員為另一個(gè)級聯(lián)的類時(shí),需要在成員前使用@Valid標(biāo)簽(支持嵌套),并且提供該級聯(lián)的成員屬性的get方法,另外級聯(lián)類內(nèi)部也需要提供需要驗(yàn)證的成員屬性的get方法(不驗(yàn)證的成員不用get方法)
第一層實(shí)體類:
/**
* 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;
}
}第二層實(shí)體類:
/**
* 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;//不驗(yàn)證,不需get方法
@Valid
public D3param d3;
public int getI() {//成員需要get方法
return i;
}
public D3param getD3() {//級聯(lián)對象需要get方法
return d3;
}
}第三層實(shí)體類:
/**
* 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";
}測試:


自定義注解
定義一個(gè)注解,使用@Retention、@Target、@Constraint標(biāo)簽注釋,并攜帶三個(gè)方法message()、groups()、payload(),并在@Constraint標(biāo)簽里使用validatedBy屬性指定自定義驗(yàn)證類,自定義驗(yàn)證類實(shí)現(xiàn)ConstraintValidator<A extends Annotation, T>接口的boolean isValid(T value, ConstraintValidatorContext context)方法,判斷驗(yàn)證是否通過;
自定義注解:(功能:驗(yàn)證是偶數(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 "應(yīng)該是偶數(shù)";
Class<?>[] groups() default {};
Class<? extends Payload>[] payload() default {};
}對應(yīng)的驗(yàn)證類:
/**
* 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;
}
}實(shí)體類:
/**
* 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";
}測試:


到此這篇關(guān)于關(guān)于SpringBoot中controller參數(shù)校驗(yàn)的使用的文章就介紹到這了,更多相關(guān)SpringBoot controller參數(shù)校驗(yàn)內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
Java變量的初始化及靜態(tài)方法的實(shí)現(xiàn)
這篇文章主要介紹了Java變量的初始化及靜態(tài)方法的實(shí)現(xiàn),文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-10-10
Java的微信開發(fā)中使用XML格式和JSON格式數(shù)據(jù)的示例
這篇文章主要介紹了Java微信開發(fā)中使用XML格式和JSON格式數(shù)據(jù)的示例,注意一下json-lib所需要的jar包,需要的朋友可以參考下2016-02-02
通過實(shí)例了解java spring使用構(gòu)造器注入的原因
這篇文章主要介紹了通過實(shí)例了解spring使用構(gòu)造器注入的原因,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2019-12-12
java中實(shí)現(xiàn)list或set轉(zhuǎn)map的方法
這篇文章主要介紹了java中實(shí)現(xiàn)list或set轉(zhuǎn)map的方法的相關(guān)資料,需要的朋友可以參考下2017-01-01
SpringCloud使用Nacos保存和讀取變量的配置方法
在使用SpringCloud開發(fā)微服務(wù)時(shí),經(jīng)常會(huì)遇到一些比較小的后臺(tái)參數(shù)配置,這些配置不足以單獨(dú)開一張表去存儲(chǔ),而且其他服務(wù)會(huì)讀取該參數(shù),這篇文章主要介紹了SpringCloud使用Nacos保存和讀取變量,需要的朋友可以參考下2022-07-07
微服務(wù)架構(gòu)設(shè)計(jì)RocketMQ基礎(chǔ)及環(huán)境整合
這篇文章主要介紹了微服務(wù)架構(gòu)設(shè)計(jì)入門RocketMQ的基礎(chǔ)及環(huán)境整合實(shí)現(xiàn)步驟,有需要的朋友可以借鑒參考下,希望能夠有所幫助,祝大家多多進(jìn)步2021-10-10
使用cmd根據(jù)WSDL網(wǎng)址生成java客戶端代碼的實(shí)現(xiàn)
這篇文章主要介紹了使用cmd根據(jù)WSDL網(wǎng)址生成java客戶端代碼的實(shí)現(xiàn)方式,具有很好的參考價(jià)值,希望對大家有所幫助。一起跟隨小編過來看看吧2021-03-03
注解@TableName,@TableField,pgsql的模式對應(yīng)方式
這篇文章主要介紹了注解@TableName,@TableField,pgsql的模式對應(yīng)方式,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2024-04-04

