如何在Controller中使用Validated验证集合内的DTO对象?
问题描述
我有一个带分组验证的ContinentDto,代码如下:
@Data public class ContinentDto { @Null(groups = { CreateValidation.class }, message = "ID must be null") @NotNull(groups = { UpdateValidation.class }, message = "ID must not be null") @JsonProperty private Integer id; @NotBlank(groups = { CreateValidation.class, UpdateValidation.class }, message = "continentName must not be blank") @JsonProperty private String continentName; public interface CreateValidation { // validation group marker interface } public interface UpdateValidation { // validation group marker interface } }
该DTO通过CreateValidation和UpdateValidation两个分组定义了不同的验证规则。单个DTO作为请求参数时验证正常,但当请求参数是DTO集合时,验证完全不生效。
我的Controller代码如下:
@RestController @RequiredArgsConstructor @RequestMapping("/api/continent") public class ContinentRestController { private final ContinentService service; @PostMapping(value = "/save-one", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Integer save( @Validated(ContinentDto.CreateValidation.class) @RequestBody final ContinentDto dto) throws TechnicalException { return service.save(dto); } @PostMapping(value = "/save-all", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Collection<Integer> saveAll( @Validated(ContinentDto.CreateValidation.class) @RequestBody final Collection<ContinentDto> dtos) throws TechnicalException { return service.save(dtos); } @PutMapping(value = "/update-one", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Integer update( @Validated(ContinentDto.UpdateValidation.class) @RequestBody final ContinentDto dto) throws FunctionalException, TechnicalException { return service.update(dto); } @PutMapping(value = "/update-all", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Collection<Integer> updateAll( @Validated(ContinentDto.UpdateValidation.class) @RequestBody final Collection<ContinentDto> dtos) throws FunctionalException, TechnicalException { return service.update(dtos); } }
我尝试过在集合的尖括号内添加@Valid注解,但没有效果。也看到过建议在类级别添加@Validated的方案,但由于我使用了验证分组,这种方式无法灵活指定不同方法的分组,特此寻求解决方法。
解决方法
方法一:使用集合包装类(推荐,兼容性好)
Spring Validation默认不支持直接对顶层集合类型做验证,需要将集合包装到一个DTO类中,通过级联验证触发集合内元素的分组验证:
- 创建集合包装类,在集合字段上添加
@Valid注解开启级联验证:
@Data public class ContinentDtoList { @Valid private List<ContinentDto> continents; }
- 修改Controller中的批量方法,使用包装类作为请求参数,并通过
@Validated指定对应分组:
@PostMapping(value = "/save-all", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Collection<Integer> saveAll( @Validated(ContinentDto.CreateValidation.class) @RequestBody final ContinentDtoList dtoList) throws TechnicalException { return service.save(dtoList.getContinents()); } @PutMapping(value = "/update-all", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Collection<Integer> updateAll( @Validated(ContinentDto.UpdateValidation.class) @RequestBody final ContinentDtoList dtoList) throws FunctionalException, TechnicalException { return service.update(dtoList.getContinents()); }
这样Spring会自动遍历continents集合中的每个ContinentDto,并应用指定分组的验证规则。
方法二:结合@Validated和@Valid直接验证集合(Spring 5.2+支持)
如果你的Spring版本在5.2及以上,可以直接在集合参数前添加@Valid注解,配合@Validated指定分组,无需包装类:
修改Controller的批量方法如下:
@PostMapping(value = "/save-all", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Collection<Integer> saveAll( @Validated(ContinentDto.CreateValidation.class) @Valid @RequestBody final Collection<ContinentDto> dtos) throws TechnicalException { return service.save(dtos); } @PutMapping(value = "/update-all", consumes = MediaType.APPLICATION_JSON_VALUE, produces = MediaType.APPLICATION_JSON_VALUE) public @ResponseBody Collection<Integer> updateAll( @Validated(ContinentDto.UpdateValidation.class) @Valid @RequestBody final Collection<ContinentDto> dtos) throws FunctionalException, TechnicalException { return service.update(dtos); }
这里的@Valid用于触发集合内元素的级联验证,@Validated指定要应用的验证分组,两者结合即可实现集合元素的分组验证。
注意:如果你的Spring版本低于5.2,这种方式可能不生效,建议优先使用方法一。
内容的提问来源于stack exchange,提问作者Paul Marcelin Bejan
相关产品推荐
相关产品推荐

