使用@ModelAttribute接收含List字段的DTO时类型转换失败
问题分析
你遇到的报错Failed to convert value of type "java.lang.String" to required type "java.lang.List",核心原因是Spring的@ModelAttribute默认仅支持传统表单键值对格式的参数绑定,而前端通过JSON.stringify([])将children序列化为JSON字符串传递,Spring无法自动将该字符串转换为List<ChildDTO>类型。
以下是三种可行的解决办法:
方法一:后端添加字符串转List的转换器
编写一个Spring Converter,实现将JSON字符串转换为List<ChildDTO>的逻辑,Spring会自动使用该转换器处理children参数:
@Component public class StringToChildDTOListConverter implements Converter<String, List<ChildDTO>> { private final ObjectMapper objectMapper; // 注入Spring自带的ObjectMapper实例 public StringToChildDTOListConverter(ObjectMapper objectMapper) { this.objectMapper = objectMapper; } @Override public List<ChildDTO> convert(String source) { try { // 使用TypeReference指定泛型类型,避免类型擦除问题 return objectMapper.readValue(source, new TypeReference<List<ChildDTO>>() {}); } catch (JsonProcessingException e) { throw new IllegalArgumentException("children参数格式错误,需传入合法JSON数组", e); } } }
添加该转换器后,前端无需修改代码,Spring会自动完成JSON字符串到List的转换。
方法二:前端调整表单参数格式
放弃JSON序列化,将children数组中的每个对象拆分为带索引的表单字段,Spring的@ModelAttribute原生支持这种索引式参数绑定:
const formData = new FormData(); // 示例子对象数组 const children = [{id: 1, name: "张三"}, {id: 2, name: "李四"}]; children.forEach((child, index) => { formData.append(`children[${index}].id`, child.id); formData.append(`children[${index}].name`, child.name); }); // 添加其他字段 // formData.append("otherField", "value"); axios.put("http://localhost:8080/api", formData);
这种方式无需修改后端代码,Spring会自动将参数绑定到MyObjectDTO的children列表中。
方法三:改用@RequestPart分离DTO和文件
如果DTO中包含文件字段,可以将DTO与文件通过@RequestPart分别接收,Spring会使用JSON消息转换器处理DTO,天然支持List类型解析:
后端接口修改:
import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.PutMapping; import org.springframework.web.bind.annotation.RequestPart; import org.springframework.web.multipart.MultipartFile; @PutMapping(consumes = MediaType.MULTIPART_FORM_DATA_VALUE) ResponseEntity<MyObject> updateMyObject( @RequestPart("myObjectDTO") MyObjectDTO myObjectDTO, @RequestPart(value = "file", required = false) MultipartFile file // 按需添加文件参数 ) { // 业务逻辑处理 return ResponseEntity.ok(...); }
前端请求修改:
const formData = new FormData(); // 构造完整DTO对象 const dto = { // 其他字段 children: [{id: 1, name: "张三"}] }; // 将DTO转换为Blob作为独立表单Part formData.append('myObjectDTO', new Blob([JSON.stringify(dto)], {type: 'application/json'})); // 若存在文件则添加 // formData.append('file', yourFile); axios.put("http://localhost:8080/api", formData, { headers: { 'Content-Type': 'multipart/form-data' } });
内容的提问来源于stack exchange,提问作者iPhoneJavaDev
相关产品推荐
相关产品推荐

