🚀 前端字段名和后端不一致?解鎖 JSON 映射的“隱藏規則” 🌟
嘿,技術冒險家們!👋 今天我們要聊一個開發中常見的“坑”:前端傳來的 JSON 參數字段名和后端對象字段名不一致,會發生什么?是默默失敗,還是直接炸裂?💥 我將以 Spring 的 PageWithSearch
為例,帶你揭開 Jackson 的神秘面紗,還有流程圖助陣,快跟我一起探索吧!💪
🎯 第一幕:一場“命名失誤”的意外
問題起源
我在開發一個分頁查詢接口,前端傳了個 JSON:
{"searchField": "name","searchValue": "John","pageNum": 0,"pageSize": 10
}
后端用 PageWithSearch
接收:
@PostMapping("/listInviteCodeByPageWithSearch")
public BaseResult listInviteCodeByPageWithSearch(@Valid @RequestBody PageWithSearch pageWithSearch) {Page<InviteCode> inviteCodePage = inviteCodeService.findPaginatedInviteCodeByAdminIdAndSearch(7, pageWithSearch);return BaseResult.success(inviteCodePage);
}
結果,服務層拋了個 NullPointerException
,接口返回:
{"code": 500,"msg": "分頁查詢失敗:null","data": null
}
啥?明明傳了 pageNum
和 pageSize
,怎么炸了?🤔
🔍 第二幕:深入代碼,尋找線索
關鍵代碼
服務層:
public Page<InviteCode> findPaginatedInviteCodeByAdminIdAndSearch(Integer adminId, PageWithSearch pageWithSearch) {PageRequest pageRequest = PageRequest.of(pageWithSearch.getPage(), pageWithSearch.getPageSize());// ... 其他邏輯 ...
}
PageWithSearch
和 BasePage
:
public class PageWithSearch extends BasePage {private String field;private String value;public Integer getPageSize() {return this.size;}
}public class BasePage {Integer page;Integer size;public Integer getPage() {return page;}public Integer getSize() {return size;}
}
初步分析
- 前端字段:
pageNum
、pageSize
。 - 后端字段:
page
、size
。 - 問題:字段名不一致,
pageWithSearch.getPage()
和getPageSize()
返回null
,導致PageRequest.of
自動拆箱時拋出NullPointerException
。
🐞 第三幕:Jackson 的“嚴格規則”
真相揭曉
Spring 默認用 Jackson 處理 JSON 到對象的映射,它的規則很簡單:
- 字段名必須一致:JSON 字段名與 Java 對象字段名大小寫敏感匹配。
- 不一致的結果:未匹配的字段被忽略,對象中對應字段保持默認值(
null
)。
測試驗證
輸入:
{"pageNum": 0,"pageSize": 10
}
pageWithSearch.getPage()
→null
(無page
字段)。pageWithSearch.getPageSize()
→null
(無size
字段)。PageRequest.of(null, null)
→ 自動拆箱 →NullPointerException
。
Mermaid 流程圖:映射失敗過程
🔧 第四幕:解決“命名沖突”
為什么會這樣?
- Jackson 的默認行為:嚴格匹配,不做自動轉換。
- 后端代碼:未處理字段名不一致,導致
null
值引發下游問題。
解決方案
方案 1:用 @JsonProperty
指定映射
修改 BasePage
和 PageWithSearch
:
public class BasePage {@JsonProperty("pageNum")Integer page;@JsonProperty("pageSize")Integer size;// ... 其他代碼 ...
}public class PageWithSearch extends BasePage {@JsonProperty("searchField")private String field;@JsonProperty("searchValue")private String value;// ... 其他代碼 ...
}
- 效果:
- 前端用
pageNum
、pageSize
、searchField
,后端正確映射。 - 輸入:
{"searchField": "name","pageNum": 0,"pageSize": 10 }
pageWithSearch.getPage()
→0
pageWithSearch.getPageSize()
→10
- 前端用
方案 2:全局命名策略
在 application.yml
中配置:
spring:jackson:property-naming-strategy: SNAKE_CASE
- 效果:
- 前端用
page_num
、page_size
,自動映射到page
、size
。 - 輸入:
{"field": "name","page_num": 0,"page_size": 10 }
- 前端用
方案 3:服務層防御
即使字段名不一致,也避免異常:
public Page<InviteCode> findPaginatedInviteCodeByAdminIdAndSearch(Integer adminId, PageWithSearch pageWithSearch) {Pageable pageable = pageWithSearch.toPageableWithDefault(0, 10); // 默認值保護String field = pageWithSearch.getField();String value = pageWithSearch.getValue();if (!StringUtils.isEmpty(field) && !StringUtils.isEmpty(value)) {return inviteCodeRepository.findPaginatedInviteCodeByAdminIdAndFieldAndValue(adminId, field, value, pageable);} else {return inviteCodeRepository.findByAdminId(adminId, pageable);}
}
- 效果:
page
或size
為null
時,用默認值 0 和 10。
Mermaid 流程圖:修復過程
🌈 第五幕:經驗與啟發
學到了啥?💡
- Jackson 的嚴格匹配:
- 字段名不一致,后端字段變
null
,小心下游邏輯!
- 字段名不一致,后端字段變
- 命名約定很重要:
- 前后端要統一字段名,或者用工具橋接差異。
- 防御性編程:
null
是隱患,提前處理是王道。
小建議 🌟
- 文檔化:
- 用 Swagger 或 API 文檔明確字段名,減少誤解。
- 日志排查:
log.info("Received: page={}, size={}", pageWithSearch.getPage(), pageWithSearch.getPageSize());
🎬 尾聲
從 500
錯誤的迷霧到揭開字段名不一致的真相,這場 debug 讓我對 JSON 映射有了新認識。希望這篇博客能幫你在前后端對接時少踩坑!有問題歡迎留言,咱們一起聊技術!??