简介
本文介绍如何解决@Valid放在Controller的List类型的参数上时校验无效的问题。
问题复现
代码
Controller
package com.knife.example.business.user.controller; import com.knife.example.business.user.bo.UserBO; import com.knife.example.common.util.ValidateUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; import java.util.List; @Api(tags = "测试list") @RestController @RequestMapping("list") public class ValidListController { /** * 如果入参是List类型,则必须是@RequestBody,否则会报错 */ @ApiOperation("valid测试基础列表") @PostMapping("validBaseList") public List<String> validBaseList(@Valid @RequestBody @NotEmpty List<String> nameList) { return nameList; } @ApiOperation("valid测试对象列表") @PostMapping("validObjectList") public List<UserBO> validObjectList(@Valid @RequestBody @NotEmpty List<UserBO> userBOList) { return userBOList; } @ApiOperation("valid测试手动校验工具") @PostMapping("validManualObjectList") public List<UserBO> validManualObjectList(@RequestBody List<UserBO> userBOList) { ValidateUtil.validate(userBOList); return userBOList; } }
BO
package com.knife.example.business.user.bo; import lombok.Data; import javax.validation.Valid; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotEmpty; import javax.validation.constraints.NotNull; import java.util.List; @Data public class UserBO { @NotBlank(message = "名字不能为空") private String name; private Integer age; @NotBlank(message = "密码不能为空") private String password; @NotEmpty(message = "分数不能为空") private List<Integer> scoreArray; @Valid @NotNull(message = "账户不能为空") private AccountBO accountBO; @Valid @NotEmpty(message = "账户列表不能为空") private List<AccountBO> accountBOList; }
package com.knife.example.business.user.bo; import lombok.Data; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; @Data public class AccountBO { @NotNull(message = "账户ID不能为空") private Long id; @NotBlank(message = "电话号码不能为空") private String phoneNumber; private String[] emails; }
测试
从下图可以看到,所有的校验都没有生效。
测试1:空列表
测试2:valid校验对象列表
入参:
[ { "accountBO": { "emails": [], "id": 2, "phoneNumber": "" }, "accountBOList": [ { "emails": [], "id": null, "phoneNumber": "12345" } ], "age": 0, "name": "", "password": "", "scoreArray": [] } ]
测试3:手动校验对象列表
入参:
[ { "accountBO": { "emails": [], "id": 2, "phoneNumber": "" }, "accountBOList": [ { "emails": [], "id": null, "phoneNumber": "12345" } ], "age": 0, "name": "", "password": "", "scoreArray": [] } ]
原因分析
@Valid只能校验Java Bean(单个对象),而List<E>不是JavaBean所以校验会失败。
解决方案
方案1:@Validated + @Valid(推荐)
很简单,在接口类上加@Validated即可。
代码
package com.knife.example.business.user.controller; import com.knife.example.business.user.bo.UserBO; import com.knife.example.common.util.ValidateUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; import java.util.List; @Validated @Api(tags = "测试list") @RestController @RequestMapping("list") public class ValidListController { /** * 如果入参是List类型,则必须是@RequestBody,否则会报错 */ @ApiOperation("valid测试基础列表") @PostMapping("validBaseList") public List<String> validBaseList(@Valid @RequestBody @NotEmpty List<String> nameList) { return nameList; } @ApiOperation("valid测试对象列表") @PostMapping("validObjectList") public List<UserBO> validObjectList(@Valid @RequestBody @NotEmpty List<UserBO> userBOList) { return userBOList; } @ApiOperation("valid测试手动校验工具") @PostMapping("validManualObjectList") public List<UserBO> validManualObjectList(@RequestBody List<UserBO> userBOList) { ValidateUtil.validate(userBOList); return userBOList; } }
测试
可以看到:自动校验都成功了。(手动校验仍然失效)。
测试1:空列表(有效)
测试2:valid校验对象列表(有效)
入参:
[ { "accountBO": { "emails": [], "id": 2, "phoneNumber": "" }, "accountBOList": [ { "emails": [], "id": null, "phoneNumber": "12345" } ], "age": 0, "name": "", "password": "", "scoreArray": [] } ]
测试3:手动校验对象列表(无效)
入参:
[ { "accountBO": { "emails": [], "id": 2, "phoneNumber": "" }, "accountBOList": [ { "emails": [], "id": null, "phoneNumber": "12345" } ], "age": 0, "name": "", "password": "", "scoreArray": [] } ]
如上所示,校验无效,解决方法见:本文最后。
方案2:自定义List
新建一个类,实现List接口,使这个类即具有了JavaBean的特性,又具有了List的特性。
这样写有些麻烦,要实现几十个方法。
package com.example.demo.common; import javax.validation.Valid; import java.util.*; public class ValidList<E> implements List<E> { @Valid private List<E> list; public ValidList() { this.list = new ArrayList<>(); } public ValidList(List<E> list) { this.list = list; } public List<E> getList() { return list; } public void setList(List<E> list) { this.list = list; } @Override public int size() { return list.size(); } @Override public boolean isEmpty() { return list.isEmpty(); } // 实现其他方法 }
用法
Controller将原来的List改为ValidList即可:
package com.knife.example.business.user.controller; import com.knife.example.business.user.bo.UserBO; import com.knife.example.common.util.ValidateUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; import java.util.List; @Api(tags = "测试list") @RestController @RequestMapping("list") public class ValidListController { /** * 如果入参是List类型,则必须是@RequestBody,否则会报错 */ @ApiOperation("valid测试基础列表") @PostMapping("validBaseList") public List<String> validBaseList(@Valid @RequestBody @NotEmpty ValidList<String> nameList) { return nameList; } @ApiOperation("valid测试对象列表") @PostMapping("validObjectList") public List<UserBO> validObjectList(@Valid @RequestBody @NotEmpty ValidList<UserBO> userBOList) { return userBOList; } @ApiOperation("valid测试手动校验工具") @PostMapping("validManualObjectList") public List<UserBO> validManualObjectList(@RequestBody ValidList<UserBO> userBOList) { ValidateUtil.validate(userBOList); return userBOList; } }
方案3:包装List
把List封装成Java Bean,定义一个ListWrapper类。
包装List
package com.example.demo.business.validAndValidated.common; import lombok.Data; import lombok.Getter; import lombok.Setter; import javax.validation.Valid; import java.util.ArrayList; import java.util.List; @Data public class ListWrapper<E> { @Valid private List<E> list; public ListWrapper() { list = new ArrayList<>(); } public ListWrapper(List<E> list) { this.list = list; } }
用法
- 将Controller的List换成ListWrapper;
- 传参的时候[{},{}..]要改为{“list”: [{},{}..]}
package com.knife.example.business.user.controller; import com.knife.example.business.user.bo.UserBO; import com.knife.example.common.util.ValidateUtil; import io.swagger.annotations.Api; import io.swagger.annotations.ApiOperation; import org.springframework.validation.annotation.Validated; import org.springframework.web.bind.annotation.*; import javax.validation.Valid; import javax.validation.constraints.NotEmpty; import java.util.List; @Api(tags = "测试list") @RestController @RequestMapping("list") public class ValidListController { /** * 如果入参是List类型,则必须是@RequestBody,否则会报错 */ @ApiOperation("valid测试基础列表") @PostMapping("validBaseList") public List<String> validBaseList(@Valid @RequestBody @NotEmpty ListWrapper<String> nameList) { return nameList; } @ApiOperation("valid测试对象列表") @PostMapping("validObjectList") public List<UserBO> validObjectList(@Valid @RequestBody @NotEmpty ListWrapper<UserBO> userBOList) { return userBOList; } @ApiOperation("valid测试手动校验工具") @PostMapping("validManualObjectList") public List<UserBO> validManualObjectList(@RequestBody ListWrapper<UserBO> userBOList) { ValidateUtil.validate(userBOList); return userBOList; } }
解决手动校验List无效的问题
只能手动遍历列表,挨个去校验单个对象。
代码
原来的ValidateUtil
package com.knife.example.common.util; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.ValidationException; import javax.validation.Validator; import java.util.Set; /** * hibernate validator的校验工具 */ public class ValidateUtil { private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); /** * 校验实体类 */ public static <T> void validate(T t) { Set<ConstraintViolation<T>> constraintViolations = validator.validate(t); if (constraintViolations.size() > 0) { StringBuilder validateError = new StringBuilder(); for (ConstraintViolation<T> constraintViolation : constraintViolations) { validateError.append(constraintViolation.getMessage()).append(";"); } throw new ValidationException(validateError.toString()); } } /** * 通过组来校验实体类 */ public static <T> void validate(T t, Class<?>... groups) { Set<ConstraintViolation<T>> constraintViolations = validator.validate(t, groups); if (constraintViolations.size() > 0) { StringBuilder validateError = new StringBuilder(); for (ConstraintViolation<T> constraintViolation : constraintViolations) { validateError.append(constraintViolation.getMessage()).append(";"); } throw new ValidationException(validateError.toString()); } } }
修改后的ValidateUtil(加了个重载)
package com.knife.example.common.util; import javax.validation.ConstraintViolation; import javax.validation.Validation; import javax.validation.ValidationException; import javax.validation.Validator; import java.util.Collection; import java.util.Set; /** * hibernate validator的校验工具 */ public class ValidateUtil { private static final Validator validator = Validation.buildDefaultValidatorFactory().getValidator(); /** * 校验实体类 */ public static <T> void validate(T t) { Set<ConstraintViolation<T>> constraintViolations = validator.validate(t); if (constraintViolations.size() > 0) { StringBuilder validateError = new StringBuilder(); for (ConstraintViolation<T> constraintViolation : constraintViolations) { validateError.append(constraintViolation.getMessage()).append(";"); } throw new ValidationException(validateError.toString()); } } /** * 校验实体类集合 */ public static <T> void validate(Collection<T> tCollection) { for (T t : tCollection) { validate(t); } } /** * 通过组来校验实体类 */ public static <T> void validate(T t, Class<?>... groups) { Set<ConstraintViolation<T>> constraintViolations = validator.validate(t, groups); if (constraintViolations.size() > 0) { StringBuilder validateError = new StringBuilder(); for (ConstraintViolation<T> constraintViolation : constraintViolations) { validateError.append(constraintViolation.getMessage()).append(";"); } throw new ValidationException(validateError.toString()); } } /** * 通过组来校验实体类列表 */ public static <T> void validate(Collection<T> tCollection, Class<?>... groups) { for (T t : tCollection) { validate(t, groups); } } }
测试
如下图所示:校验生效了!
入参:
[ { "accountBO": { "emails": [], "id": 2, "phoneNumber": "" }, "accountBOList": [ { "emails": [], "id": null, "phoneNumber": "12345" } ], "age": 0, "name": "", "password": "", "scoreArray": [] } ]
下载源码
此隐藏内容仅限VIP查看升级VIP
请先
!