目录
[9 前后端数据处理格式的注意事项](#9 前后端数据处理格式的注意事项)
[9.1 后端 JSON 序列化字段不一致问题](#9.1 后端 JSON 序列化字段不一致问题)
[(2)@JsonProperty指定 JSON 字段名](#(2)@JsonProperty指定 JSON 字段名)
[(3)隐藏敏感字段(如 password)](#(3)隐藏敏感字段(如 password))
[9.2 前端响应格式适配问题](#9.2 前端响应格式适配问题)
[9.3 后端请求参数接收规范(DTO vs Map)](#9.3 后端请求参数接收规范(DTO vs Map))
[(3)代码示例(规范 DTO 用法)](#(3)代码示例(规范 DTO 用法))
9 前后端数据处理格式的注意事项
9.1 后端 JSON 序列化字段不一致问题
- 问题表现
后端 VO 类变量名与接口文档约定的 JSON 字段名不匹配(如接口定义meta,后端写metaVo),导致 Jackson 序列化后 JSON 字段名错误,前端无法解析数据。 - 底层原理
SpringMVC 通过 Jackson 将 Java 对象序列化为 JSON,核心规则如下:
- Jackson 序列化时,不直接读取 Java 变量名,而是通过解析类的getter方法名来生成 JSON 字段名,推导规则固定:
- 第一步:去掉getter方法名的get前缀(如getMeta()去掉get→Meta,getMetaVo()去掉get→MetaVo);
- 第二步:将剩余部分的首字母小写,得到最终 JSON 字段名(Meta→meta,MetaVo→metaVo)。
- IDEA 手动生成 getter、@Data(Lombok)自动生成 getter都是get + 变量名首字母大写 + 变量剩余部分
- JSON 字段名大小写敏感,需与前端完全一致。
- 解决方案及拓展
(1)修改变量名
-
解决方案:变量名严格对齐接口文档 JSON 字段名
-
代码示例:
//假设接口文档中对应的变量名是meta
// 错误写法
// private MetaVO metaVo;
// 正确写法
private MetaVO meta; -
原理说明:保证getter生成的字段名与接口一致
(2)@JsonProperty指定 JSON 字段名
-
解决方案:使用@JsonProperty指定 JSON 字段名
-
代码示例:
@JsonProperty("user_id")
private Long userId;
原理说明:覆盖 Jackson 默认规则,强制定义字段名
(3)隐藏敏感字段(如 password)
-
解决方案:使用@JsonIgnore注解
-
代码示例:
@JsonIgnore
private String password; -
原理说明:序列化时跳过该字段,JSON 中不生成对应键值对
(4)空值字段不返回前端
-
解决方案:全局配置(application.yml)或@JsonInclude注解
-
代码示例(全局配置):
spring:
jackson:
default-property-inclusion: non_null -
原理说明:控制空值(null / 空字符串 / 空集合)是否序列化
9.2 前端响应格式适配问题
- 问题表现
- 后端成功传参,但是前端就是拿不到值
- 前处理的字段名和后端处理的字段名不一致
- 原因
-
前端可能封装统一 axios 工具类,拦截响应自动解析data
代码示例:
响应拦截器会拦截所有的响应,处理数据后再统一返回,此时我前端调后端接口拿到的数据就是我响应拦截器处理后的数据不是后端返回的原始数据。import axios from 'axios'
const request = axios.create({ baseURL: '/api' })// 响应拦截器:自动提取Result<T>中的data
request.interceptors.response.use(
res => res.data, // 直接返回data字段,业务层无需再res.data
error => {
console.error('请求失败:', error)
return Promise.reject(error)
}
)// 修正后的购物车数量更新方法
const updateCartCount = async () => {
try {
// 拦截器已解析data,直接获取业务数据
const cartItems = await request.get('/cart/items')
cartItemCount.value = Array.isArray(cartItems)
? cartItems.reduce((total, item) => total + item.quantity, 0)
: 0
} catch (error) {
cartItemCount.value = 0
}
}
- 解决方案
-
前后端字段名不一致
解决方案:前端手动映射字段名(临时方案),优先后端用@JsonProperty对齐(长期方案)
代码示例(前端映射):
解构后端数据并重命名字段,适配前端变量命名习惯// 后端字段itemName/unitPrice → 前端name/price
const mappedItems = items.map(item => ({
...item,
name: item.itemName,
price: item.unitPrice,
unitPrice: item.unitPrice,
totalPrice: item.totalPrice
}))
9.3 后端请求参数接收规范(DTO vs Map)
- 问题表现
- 使用Map接收表单参数时,会包含按钮、隐藏域等无关字段,需手动过滤;
- 未按规范使用 DTO 接收参数,导致参数校验、类型安全无法保障;
- 混淆 DTO(接收参数)、VO(返回数据)、Entity(数据库映射)的职责,数据类使用混乱。
- 底层原理
- SpringMVC 参数绑定规则:DTO 按字段名匹配赋值(自动过滤无关参数),Map存储全量键值对;
- DTO 为强类型(编译期校验),Map为弱类型(运行期报错);
- 不同数据类有明确职责边界,混用会导致数据泄露、校验缺失。
- DTO 与 Map 详细对比
|------|-----------------------------|-----------------|---------------------------|
| 对比维度 | 专用 DTO 类接收 | Map 集合接收 | 核心影响 |
| 字段过滤 | 自动过滤无关参数(如表单按钮) | 接收所有参数(含无关字段) | Map 需手动过滤,易遗漏;DTO 无需额外处理 |
| 类型安全 | 编译期校验(如 String→Integer 报错) | 运行期类型转换异常 | DTO 提前暴露错误,Map 线上才会发现问题 |
| 参数校验 | 支持 @NotBlank / @NotNull 等注解 | 需手动编写校验逻辑 | DTO 简化校验,Map 增加冗余代码 |
| 可读性 | 字段显性定义,易协作 | 字段隐性存储,需查文档 | DTO 降低团队沟通成本,Map 易导致理解偏差 |
| 适用场景 | 表单 / 接口参数接收(90% 场景) | 临时调试、动态参数(极少场景) | DTO 适配结构化参数,Map 仅用于非结构化场景 |
- 规范要求
(1)数据类职责划分(强制)
|--------|---------------------|---------------------|
| 数据类类型 | 核心职责 | 禁用场景 |
| Entity | 对应数据库表,仅用于数据层 | 直接接收前端参数、返回给前端 |
| DTO | 接收前端参数,含参数校验,仅存必要字段 | 映射数据库、返回给前端(特殊场景除外) |
| VO | 返回前端数据,隐藏敏感字段 | 接收前端参数、映射数据库 |
(2)参数接收规范
- 所有表单 / 接口参数强制使用 DTO 接收(如UserLoginDTO、CartAddDTO),仅包含接口所需字段;
- 禁止业务接口用Map接收 / 返回参数(临时调试除外);
- DTO 需添加参数校验注解(如@NotBlank校验非空、@Pattern校验格式),替代手动校验。
(3)代码示例(规范 DTO 用法)
// 购物车添加参数DTO(仅含必要字段)
@Data
public class CartAddDTO {
@NotNull(message = "商品ID不能为空")
private Long productId;
@Min(value = 1, message = "数量不能小于1")
private Integer quantity;
// 仅包含接口所需字段,自动过滤表单无关参数(如按钮、隐藏域)
}
// Controller接收参数(自动过滤无关字段+校验)
@PostMapping("/cart/add")
public Result<Void> addCart(@Valid @RequestBody CartAddDTO cartAddDTO) {
cartService.addCart(cartAddDTO);
return Result.success();
}