<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
引數校驗主要使用兩個標籤@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(指定分組)屬性:
在controller類上新增@Validated標籤,在方法的引數前加驗證標籤,並且同一個引數可以新增多個標籤;
啟動類:(使用預設設定,埠8080)
package testspringboot.test6paramvalidation; import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; @SpringBootApplication public class Test6Main { /** * @param args */ public static void main(String[] args) { SpringApplication.run(Test6Main.class, args); } }
controller類:
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; @RestController @RequestMapping("/test6") @Validated public class Test6Controller { @RequestMapping("/a") public String a(@NotNull(message = "引數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();//返回錯誤描述 } }
再次測試結果:
預設驗證所有引數,即使前面驗證不通過也會繼續驗證,可以設定快速失敗,使驗證失敗立即返回不繼續驗證;
自定義注入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類的方法實體類引數前加@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引數不能超過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類的方法引數裡接收,引數裡使用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的訊息體接收引數也一樣,在引數前多加一個@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引數,groups是一個class集合,一個標籤可以設定多個group,在controller類裡方法的引數前的@Validated標籤裡使用value指定要使用的group驗證(可以指定多個group驗證),沒有設定groups的標籤預設屬於Default.class的group,設定group的class通常使用interface,可以寫在外面或者直接寫到實體類內部;
實體類:
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; 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;
另外也可以設定動態組校驗,根據某些條件和情況設定驗證的groups,需要在實體類上新增@GroupSequenceProvider標籤指定實現了DefaultGroupSequenceProvider介面並實現介面裡getValidationGroups方法的class,getValidationGroups方法返回List<Class<?>>,即為當前請求需要使用的groups(返回值相當於controller類方法引數前@Validated標籤裡的value的作用);
例如根據實體類內boolean值指定int值使用正負數:
實體類:
package testspringboot.test6paramvalidation; import javax.validation.constraints.Negative; import javax.validation.constraints.NotNull; import javax.validation.constraints.Positive; import org.hibernate.validator.group.GroupSequenceProvider; @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:
package testspringboot.test6paramvalidation; import java.util.ArrayList; import java.util.List; import org.hibernate.validator.spi.group.DefaultGroupSequenceProvider; 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) {//該方法會呼叫多次,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"; }
測試:
實體類成員為另一個級聯的類時,需要在成員前使用@Valid標籤(支援巢狀),並且提供該級聯的成員屬性的get方法,另外級聯類內部也需要提供需要驗證的成員屬性的get方法(不驗證的成員不用get方法)
第一層實體類:
package testspringboot.test6paramvalidation; import javax.validation.Valid; import javax.validation.constraints.AssertTrue; public class D1param { @AssertTrue(message = "D1param.b必須為true") public boolean b; @Valid public D2param d2; public D2param getD2() {//級聯物件需要get方法 return d2; } }
第二層實體類:
package testspringboot.test6paramvalidation; import javax.validation.Valid; import javax.validation.constraints.Positive; public class D2param { @Positive(message = "D2param.i必須為正數") public int i; public String s;//不驗證,不需get方法 @Valid public D3param d3; public int getI() {//成員需要get方法 return i; } public D3param getD3() {//級聯物件需要get方法 return d3; } }
第三層實體類:
package testspringboot.test6paramvalidation; import javax.validation.constraints.NotNull; 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屬性指定自定義驗證類,自定義驗證類實現ConstraintValidator<A extends Annotation, T>介面的boolean isValid(T value, ConstraintValidatorContext context)方法,判斷驗證是否通過;
自定義註解:(功能:驗證是偶數)
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) public @interface EAnnotation { String message() default "應該是偶數"; Class<?>[] groups() default {}; Class<? extends Payload>[] payload() default {}; }
對應的驗證類:
package testspringboot.test6paramvalidation; import javax.validation.ConstraintValidator; import javax.validation.ConstraintValidatorContext; public class EValidator implements ConstraintValidator<EAnnotation, Integer> { @Override public boolean isValid(Integer value, ConstraintValidatorContext context) { return value % 2 == 0; } }
實體類:
package testspringboot.test6paramvalidation; 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引數校驗方法詳細講解的文章就介紹到這了,更多相關SpringBoot controller 內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45