You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在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类中,通过级联验证触发集合内元素的分组验证:

  1. 创建集合包装类,在集合字段上添加@Valid注解开启级联验证:
@Data
public class ContinentDtoList {
    @Valid
    private List<ContinentDto> continents;
}
  1. 修改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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.21 08:57:44