Spring Boot 前后端参数命名踩坑记录:小驼峰变下划线导致接收不到参数
问题现象:前端传小驼峰,后端 DTO 也是小驼峰,但接收不到参数;改成下划线反而能收到。
适用版本:Spring Boot 2.x / 3.x
阅读时间:约 5 分钟
一、问题描述
最近在项目开发中遇到一个诡异的问题:
前端传参:{ "userName": "张三", "userAge": 25 }
后端 DTO:
public class UserDTO {
private String userName;
private Integer userAge;
}
结果:后端接收到的字段全是 null
但奇怪的是,当前端把参数改成下划线命名后:
前端传参:{ "user_name": "张三", "user_age": 25 }
结果:后端正常接收到了数据!
二、问题根源
经过排查,发现问题出在 Jackson 的命名策略配置 上。
核心机制
Spring Boot 默认使用 Jackson 进行 JSON 的序列化和反序列化。当配置了以下参数时:
spring:
jackson:
property-naming-strategy: SNAKE_CASE
Jackson 会在反序列化时自动将前端传来的驼峰参数转换为下划线,导致匹配失败:
┌─────────────────────────────────────────────────────────────┐
│ 请求处理流程 │
├─────────────────────────────────────────────────────────────┤
│ 前端传参 Jackson 反序列化 后端 DTO │
│ ────── ──────────────── ──────── │
│ userName → 转为 user_name → userName ❌ 不匹配 │
│ userAge → 转为 user_age → userAge ❌ 不匹配 │
└─────────────────────────────────────────────────────────────┘
关键点
⚠️
property-naming-strategy同时影响序列化和反序列化!很多人以为它只影响后端返回给前端的格式,其实它也会影响前端传给后端的参数解析。
三、排查步骤
1️⃣ 检查配置文件
搜索项目中的 application.yml 或 application.properties:
# 全局搜索
grep -r "property-naming-strategy" .
grep -r "spring.jackson" .
常见问题配置:
# ❌ 问题配置
spring:
jackson:
property-naming-strategy: SNAKE_CASE
# 或
property-naming-strategy: CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES
2️⃣ 检查自定义配置类
搜索项目中是否有自定义的 ObjectMapper:
grep -r "ObjectMapper" .
grep -r "PropertyNamingStrategies" .
常见问题代码:
// ❌ 问题代码
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
mapper.setPropertyNamingStrategy(PropertyNamingStrategies.SNAKE_CASE);
return mapper;
}
}
3️⃣ 检查父工程或 Starter 依赖
有些公司内部的 starter 或父工程可能默认配置了命名策略,需要检查依赖传递的配置。
四、解决方案
方案一:移除命名策略配置(推荐)
适用场景:前后端统一使用小驼峰命名
# application.yml
spring:
jackson:
# ✅ 删除或注释掉这行
# property-naming-strategy: SNAKE_CASE
# ✅ 或者显式设置为 null(使用默认驼峰)
property-naming-strategy: null
// 自定义配置类修改
@Configuration
public class JacksonConfig {
@Bean
public ObjectMapper objectMapper() {
ObjectMapper mapper = new ObjectMapper();
// ✅ 移除或设置为 null
mapper.setPropertyNamingStrategy(null);
return mapper;
}
}
方案二:使用 @JsonAlias 兼容多种命名
适用场景:需要同时支持驼峰和下划线,增加容错性
public class UserDTO {
@JsonAlias({"userName", "user_name"})
private String userName;
@JsonAlias({"userAge", "user_age"})
private Integer userAge;
// getter 和 setter
}
优点:
- ✅ 同时兼容两种命名方式
- ✅ 前端改动成本低
- ✅ 不影响其他接口
缺点:
- ⚠️ 字段多时需要逐个添加注解
方案三:开启大小写不敏感
适用场景:需要增加参数匹配的容错性
spring:
jackson:
mapper:
ACCEPT_CASE_INSENSITIVE_PROPERTIES: true
注意:此配置只忽略大小写,不解决驼峰与下划线的转换问题,建议配合方案一使用。
五、方案对比
| 方案 | 配置位置 | 影响范围 | 推荐度 |
|---|---|---|---|
| 移除命名策略 | application.yml | 全局 | ⭐⭐⭐⭐⭐ |
| @JsonAlias | DTO 字段 | 单个字段 | ⭐⭐⭐⭐ |
| 大小写不敏感 | application.yml | 全局 | ⭐⭐⭐ |
| 自定义 ObjectMapper | 配置类 | 全局 | ⭐⭐⭐ |
六、最佳实践配置
以下是推荐的全局 Jackson 配置,兼顾规范性和容错性:
spring:
jackson:
# 使用默认驼峰命名(前后端统一小驼峰)
property-naming-strategy: null
# 忽略大小写,增加容错性
mapper:
ACCEPT_CASE_INSENSITIVE_PROPERTIES: true
# 日期格式
date-format: yyyy-MM-dd HH:mm:ss
time-zone: Asia/Shanghai
# 空值处理:序列化时忽略 null 字段
default-property-inclusion: non_null
# 反序列化时允许未知字段(防止前端多传参数报错)
deserialization:
fail-on-unknown-properties: false
七、验证方法
修改配置后,按以下步骤验证:
1️⃣ 重启服务
确保配置生效。
2️⃣ 前端恢复小驼峰传参
// API 请求
axios.post('/api/user', {
userName: '张三',
userAge: 25
})
3️⃣ 后端添加日志验证
@PostMapping("/user")
public Result createUser(@RequestBody UserDTO dto) {
log.info("接收到的参数:{}", dto);
log.info("userName: {}", dto.getUserName());
log.info("userAge: {}", dto.getUserAge());
return Result.success();
}
4️⃣ 检查日志输出
✅ 正常输出:
接收到的参数:UserDTO(userName=张三, userAge=25)
userName: 张三
userAge: 25
❌ 异常输出:
接收到的参数:UserDTO(userName=null, userAge=null)
八、总结
| 问题 | 原因 | 解决 |
|---|---|---|
| 小驼峰接收不到 | Jackson 配置了下划线命名策略 | 移除或修改 property-naming-strategy |
| 下划线能收到 | 命名策略将驼峰转为下划线后匹配成功 | 证明配置确实生效了 |
| 部分接口正常 | 可能某些 Controller 用了 @JsonAlias | 统一配置规范 |
核心要点
property-naming-strategy同时影响序列化和反序列化- 前后端命名规范应统一(推荐都用小驼峰)
- 配置修改后务必重启服务
- 善用 @JsonAlias 增加容错性
九、延伸思考
为什么有人要配置下划线命名?
- 数据库字段规范:数据库常用下划线,方便实体类映射
- 团队规范:有些团队要求 API 统一使用下划线
- 历史遗留:老项目迁移时的兼容需求
如果确实需要输出下划线怎么办?
可以分离序列化和反序列化策略,但这需要自定义 ObjectMapper,复杂度较高。更推荐的做法是:
前后端统一使用小驼峰,后端与数据库的映射通过 MyBatis/JPA 的配置单独处理
# MyBatis 配置(只影响数据库映射,不影响 API)
mybatis:
map-underscore-to-camel-case: true
📌 收藏本文,遇到类似问题快速排查!
💬 你有遇到过类似的前后端参数命名问题吗?欢迎在评论区分享你的解决方案!