前后端数据格式的交互方式不匹配
问题根源
后端期望的格式(
LostItemController.java
):- 使用
@RequestPart LostItemRequestDTO request
接收 JSON 对象(自动反序列化) - 使用
@RequestPart MultipartFile image
接收 文件 - 要求
Content-Type: multipart/form-data
- 使用
前端实际发送的格式:
- 将 JSON 数据以字符串形式放在
request
字段(formData.append('request', JSON.stringify(requestData))
) - 后端无法自动反序列化
request
字段中的 JSON 字符串,导致 415 错误
- 将 JSON 数据以字符串形式放在
解决方案
方案1:修改前端 - 直接发送对象而非 JSON 字符串
前端不需要手动 JSON.stringify
,直接让 FormData
按字段拆分
async submitForm() {
const formData = new FormData();
// 直接添加字段(不要包裹成 JSON 字符串)
formData.append('category', this.formData.category);
formData.append('description', this.formData.description);
formData.append('lostLocation', this.formData.lostLocation);
formData.append('lostTime', new Date(this.formData.lostTime).toISOString());
// 添加图片
if (this.formData.image) {
formData.append('image', this.formData.image);
}
try {
const response = await fetch('/api/lost', {
method: 'POST',
body: formData, // 不要手动设置 Content-Type!
});
// 处理响应...
} catch (error) {
alert('提交失败: ' + error.message);
}
}
方案2:修改后端 - 手动解析 JSON 字符串
如果必须保持前端发送 request
字段,后端需调整:
@PostMapping(value = "/lost", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Result add(
@RequestPart String request, // 改为接收字符串
@RequestPart(required = false) MultipartFile image
) {
// 手动解析 JSON
LostItemRequestDTO requestData = new ObjectMapper().readValue(request, LostItemRequestDTO.class);
// 后续逻辑...
}
后端无法反序列化前端发送的 LostItemRequestDTO
对象,具体错误为:
Cannot construct instance of `com.text.pojo.LostItemRequestDTO`
(no Creators, like default constructor, exist)
确保前后端字段名严格一致(尤其是 image
)
问题根源
后端问题:
LostItemRequestDTO
类缺少 默认构造函数 或 Jackson 反序列化所需的注解。- Spring 无法将前端传来的 JSON 数据自动转换为
LostItemRequestDTO
对象。
前端问题:
- 当前前端发送的是
FormData
,但后端期望的是 multipart/form-data
中的 JSON 对象(@RequestPart LostItemRequestDTO request
)
- 当前前端发送的是
解决办法:
修复后端 DTO 类(推荐)
在 LostItemRequestDTO
中添加 无参构造函数 和 Setter 方法
package com.text.pojo;
public class LostItemRequestDTO {
private String category;
private String description;
private String lostLocation;
private String lostTime;
// 1. 必须添加无参构造函数
public LostItemRequestDTO() {}
// 2. 必须提供所有字段的 getter/setter
public String getCategory() { return category; }
public void setCategory(String category) { this.category = category; }
// 其他字段的 getter/setter...
}
方案2:修改后端控制器(手动解析 JSON)
如果无法修改 DTO,可以手动解析 JSON:
@PostMapping(value = "/lost", consumes = MediaType.MULTIPART_FORM_DATA_VALUE)
public Result add(
@RequestPart String request, // 改为接收字符串
@RequestPart(required = false) MultipartFile image
) {
// 手动解析 JSON
ObjectMapper objectMapper = new ObjectMapper();
LostItemRequestDTO requestData = objectMapper.readValue(request, LostItemRequestDTO.class);
// 后续逻辑...
}
方案3:调整前端请求格式
如果后端坚持用 @RequestPart LostItemRequestDTO request
,前端需要 按字段拆分 FormData(不推荐,但可行):
const formData = new FormData();
formData.append('category', this.formData.category);
formData.append('description', this.formData.description);
formData.append('lostLocation', this.formData.lostLocation);
formData.append('lostTime', this.formData.lostTime);
formData.append('image', this.formData.image);
问题根源
LocalDateTime
反序列化失败:- 前端发送的
lostTime
是字符串(如"2025-06-05T20:06:13"
),但后端无法自动将其转换为LocalDateTime
。 - 需要为
LocalDateTime
配置自定义反序列化器。
- 前端发送的
DTO 构造函数冲突:
- 虽然提供了无参构造函数,但还存在全参构造函数,可能导致 Jackson 混淆。
解决方案
1. 修复 DTO 类
package com.text.pojo;
import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.databind.annotation.JsonDeserialize;
import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
import java.time.LocalDateTime;
public class LostItemRequestDTO {
private String category;
private String description;
private String lostLocation;
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss") // 指定日期格式
@JsonDeserialize(using = LocalDateTimeDeserializer.class) // 使用反序列化器
private LocalDateTime lostTime;
// 只保留无参构造函数
public LostItemRequestDTO() {}
// 移除全参构造函数(避免干扰 Jackson)
// getters/setters...
}
2. 修改前端时间格式
确保前端发送的时间字符串格式与 @JsonFormat
一致:
// 前端代码(格式化时间)
const requestData = {
category: this.formData.category,
description: this.formData.description,
lostLocation: this.formData.lostLocation,
lostTime: new Date(this.formData.lostTime).toISOString().slice(0, 19) // 格式:YYYY-MM-DDTHH:mm:ss
};
formData.append('request', JSON.stringify(requestData));
jackson 的默认行为:
- 优先尝试用无参构造函数 + setter(成功)
- 但如果类中只有全参构造函数(没有无参构造),Jackson 会尝试用它,此时必须满足:
- 参数名称 严格匹配 JSON 字段名
- 或使用
@JsonProperty
标注参数
您的实际错误:
- 虽然提供了无参构造函数,但全参构造函数的存在 干扰了 Jackson 的决策(尤其在高版本 Jackson 中)
- 更关键的是:
LocalDateTime
字段没有配置反序列化规则(主因)