SpringBootcontroller参数校验方法详细讲解
2023-01-18 05:49:10 来源:易采站长站 作者:
目录
单参数校验实体类校验分组校验嵌套校验自定义注解[email protected]@Valid;
@Valid是Hibernate的注解校验,@Validated是spring的,[email protected];这两个标签也有一些不同之处,@Valid可以标注在成员属性上也可以嵌套校验,[email protected],[email protected];
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:必须是一个小于或者小于等于设定值的数字,可以用inclusive指定是否包含数字(默认true),并且value是String类型的,支持BigDecimal、BigInteger、CharSequence、byte / short / int / long以及它们的包装类(由于舍入原因不支持double和float),null是有效的;@DecimalMin:必须是一个大于或者大于等于设定值的数字,[email protected];@Digits:设定可接受范围内的数字,必须指定integer(整数位数)和fraction(小数位数),支持BigDecimal、BigInteger、CharSequence、byte / short / int / long以及它们的包装类,null是有效的;@Email:必须是一个正确格式的邮箱,可以使用regexp指定正则表达式(默认是任意字符串),可以使用flags指定正则表达式的选项,null是有效的;@Future:必须是一个未来的瞬间、日期或时间(Now是虚拟机默认的当前时区的时间),支持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:必须是现在或未来的瞬间、日期或时间,[email protected];@Max:必须是小于等于指定值的数字,value是long类型,支持BigDecimal、BigInteger、byte / short / int / long以及它们的包装类(不支持float和double),null是有效的;@Min:必须是大于等于指定值的数字,[email protected];@Negative:必须是一个严格的负数,0为无效值,支持BigDecimal、BigInteger、byte / short / int / long / float / double以及它们的包装类,null是有效的;@NegativeOrZero,必须是负数或者0,[email protected];@NotBlank:不能为null,并且至少包含一个非空白字符,接受CharSequence;@NotEmpty:不能为null或空(集合),支持CharSequence(字符序列长度)、Collection(集合size)、Map(map size)、Array(数组长度);@NotNull:不能为null,支持所有类型;@Null:必须为null,支持所有类型;@Past:必须是一个过去的瞬间、日期或时间,[email protected];@PastOrPresent:必须是现在或过去的瞬间、日期或时间,[email protected];@Pattern:必须符合指定的正则表达式,必须使用regexp参数指定正则表达式;@Positive:必须是一个严格的正数,0为无效值,[email protected];@PositiveOrZero:必须是正数或者0,[email protected];@Size:指定元素大小必须在指定范围内(包括边界值),使用min指定下边界(默认0),使用max指定上边界(默认Integer.MAX_VALUE),支持CharSequence、Collection、Map、Array,null是有效的;
单参数校验
[email protected],在方法的参数前加验证标签,并且同一个参数可以添加多个标签;
启动类:(使用默认配置,端口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也可以使用自定义设定,[email protected],然后在类里定义各种错误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了:
实体类校验
[email protected][email protected],实体类里的属性前加验证标签,[email protected];
实体类:
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的消息体接收参数也一样,[email protected]:
@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,[email protected]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,[email protected][email protected]ionGroups方法的class,getValidationGroups方法返回List<Class<?>>,即为当前请求需要使用的groups([email protected]的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"; }
测试:
嵌套校验
实体类成员为另一个级联的类时,[email protected](支持嵌套),并且提供该级联的成员属性的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"; }
测试:
自定义注解
定义一个注解,[email protected]@[email protected],并携带三个方法message()、groups()、payload(),[email protected]证类,自定义验证类实现ConstraintValidator<A>
自定义注解:(功能:验证是偶数)
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 内容请搜索易采站长站以前的文章或继续浏览下面的相关文章希望大家以后多多支持易采站长站!
如有侵权,请发邮件到 [email protected]
最新图文推荐
相关文章
-
Spring Cloud 整合Apache-SkyWalking实现链路跟踪的方法
什么是SkyWalking 查看官网https://skywalking.apache.org/ 分布式系统的应用程序性能监视工具,专为微服务、云原生架构和基于容器(Docker、K8s、Mesos)架构而设计。 安装 进入下载页面https://2020-06-18
-
成功解决IDEA2020 Plugins 连不上、打不开的方法
IntelliJ IDEA 2020.1 插件中心一直打不开,鉴于有部分同学反馈设置http proxy不能解决,所以可按以下顺序检查 一、设置 http proxy—勾上Auto-detect proxy setting,参照下图,加上地址 http://127.0.02020-06-25
-
IDEA2020 1.1中Plugins加载不出来的问题及解决方法
进入File-Setting 如图,取消勾选,点击确认后重启,点击了以后等一会就可以正常显示 ps:下面看下解决IDEA 2020.1.1 找不到程序包和符号 问题描述 IDEA 2020.1.1 maven项目build的时候报错,找2020-06-28
-
springboot + rabbitmq 如何实现消息确认机制(踩坑经验)
本文收录在个人博客:www.chengxy-nds.top,技术资源共享,一起进步 最近部门号召大伙多组织一些技术分享会,说是要活跃公司的技术氛围,但早就看穿一切的我知道,这 T M 就是为了刷2020-07-01
-
JetBrains IntelliJ IDEA 2020安装与使用教程详解
对于JetBrains IntelliJ IDEA 2020的认识 IntelliJ IDEA 2020是一款JAVA编程软件,捷克IntelliJ公司研发推出。该软件提供了一个非常强大的JAVA集成开发环境,不仅添加了对Records的完整代码洞察支持,2020-06-28