使用FormData从JS传参时,Spring DTO嵌套类字段映射失败
FormData传输嵌套POJO到Spring后端的字段映射问题解决
问题场景
使用JavaScript的FormData向Spring后端传输包含文件和手机号列表的数据时,嵌套的PhoneNumberDto字段无法正确映射。打印结果显示id为null,phoneNo显示[object Object],具体代码如下:
前端JavaScript代码
let fdata = new FormData(); fdata.append("postalCode", $("#c-postalCode").val()); fdata.append("dob", $("#c-dob").val()); fdata.append("contactImage", $("#c-contactimg")[0].files[0]); fdata.append("phoneNos", [{id:"1",phoneNo:"12345"}]); fetch("/cm/save/contact/details", { body: fdata, method: "POST" }).then((response) => { //Somecode})
后端RestController代码
@PostMapping("/save/contact/details") public void saveContactDetails(@ModelAttribute ContactDetailsDto contactDetailsDto) { System.out.println(contactDetailsDto); }
ContactDetailsDto代码
@Data @NoArgsConstructor @AllArgsConstructor public class ContactDetailsDto { //Somecode private MultipartFile contactImage; @NotBlank private List<PhoneNumberDto> phoneNos = new ArrayList<>(); }
PhoneNumberDto代码
@Data @NoArgsConstructor @AllArgsConstructor public class PhoneNumberDto { private Integer id; private String phoneNo; public PhoneNumberDto(String phoneNo) { super(); this.phoneNo = phoneNo; } }
异常打印结果
ContactDetailsDto(id=null, name=asd, email=, contactImage=org.springframework.web.multipart.support.StandardMultipartHttpServletRequest$StandardMultipartFile@2dc05cd7, address1=, address2=, postalCode=null, country=101, state=4030, city=57709, dob=null, phoneNos=[PhoneNumberDto(id=null, phoneNo=[object Object])])
问题原因
FormData仅支持传输字符串或Blob类型数据,直接传入数组对象会被自动转为字符串[object Object],Spring的@ModelAttribute无法将该字符串解析为嵌套的PhoneNumberDto对象,导致映射失败。
解决方案
方案一:使用索引式键名构造FormData(推荐)
前端需要将列表中每个对象的属性单独append,采用phoneNos[index].propertyName的命名格式,Spring会自动识别并组装为嵌套DTO列表:
let fdata = new FormData(); fdata.append("postalCode", $("#c-postalCode").val()); fdata.append("dob", $("#c-dob").val()); fdata.append("contactImage", $("#c-contactimg")[0].files[0]); // 处理手机号列表 const phoneList = [{id:"1",phoneNo:"12345"}]; phoneList.forEach((phone, index) => { fdata.append(`phoneNos[${index}].id`, phone.id); fdata.append(`phoneNos[${index}].phoneNo`, phone.phoneNo); }); fetch("/cm/save/contact/details", { body: fdata, method: "POST" }).then((response) => { /* 后续处理逻辑 */ })
这种方式完全适配Spring@ModelAttribute的参数解析规则,无需修改后端代码。
方案二:JSON字符串传输+后端手动解析
前端将列表转为JSON字符串后append到FormData,后端通过临时字段接收并手动解析为列表:
前端代码修改
let fdata = new FormData(); fdata.append("postalCode", $("#c-postalCode").val()); fdata.append("dob", $("#c-dob").val()); fdata.append("contactImage", $("#c-contactimg")[0].files[0]); // 将列表转为JSON字符串 fdata.append("phoneNosJson", JSON.stringify([{id:"1",phoneNo:"12345"}])); fetch("/cm/save/contact/details", { body: fdata, method: "POST" }).then((response) => { /* 后续处理逻辑 */ })
后端DTO修改
@Data @NoArgsConstructor @AllArgsConstructor public class ContactDetailsDto { // 原有字段... private MultipartFile contactImage; private List<PhoneNumberDto> phoneNos = new ArrayList<>(); // 临时接收JSON字符串的方法,Spring会自动匹配请求参数中的phoneNosJson public void setPhoneNosJson(String phoneNosJson) { ObjectMapper objectMapper = new ObjectMapper(); try { // 解析JSON字符串为PhoneNumberDto列表 this.phoneNos = objectMapper.readValue(phoneNosJson, new TypeReference<List<PhoneNumberDto>>() {}); } catch (JsonProcessingException e) { e.printStackTrace(); // 可添加异常处理,比如抛出业务异常或设置默认空列表 } } }
注意:Spring Boot默认已引入Jackson依赖,若为非Spring Boot项目需手动添加Jackson相关依赖。
补充说明
前端传入的id是字符串类型"1",后端PhoneNumberDto的id为Integer类型,Spring会自动完成类型转换,无需额外处理。
内容的提问来源于stack exchange,提问作者Mr Anonymous
相关产品推荐
相关产品推荐

