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

使用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.05 01:50:21