前端字段名和后端不一致?解锁 JSON 映射的“隐藏规则” !!!

🚀 前端字段名和后端不一致?解锁 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
}

啥?明明传了 pageNumpageSize,怎么炸了?🤔


🔍 第二幕:深入代码,寻找线索

关键代码

服务层:

public Page<InviteCode> findPaginatedInviteCodeByAdminIdAndSearch(Integer adminId, PageWithSearch pageWithSearch) {PageRequest pageRequest = PageRequest.of(pageWithSearch.getPage(), pageWithSearch.getPageSize());// ... 其他逻辑 ...
}

PageWithSearchBasePage

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;}
}

初步分析

  • 前端字段pageNumpageSize
  • 后端字段pagesize
  • 问题:字段名不一致,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 流程图:映射失败过程
前端: pageNum=0, pageSize=10
Jackson 映射到 PageWithSearch
page 未匹配, size 未匹配
getPage()=null, getPageSize()=null
PageRequest.of(null, null)
自动拆箱抛 NullPointerException
返回 500: '分页查询失败:null'

🔧 第四幕:解决“命名冲突”

为什么会这样?

  • Jackson 的默认行为:严格匹配,不做自动转换。
  • 后端代码:未处理字段名不一致,导致 null 值引发下游问题。

解决方案

方案 1:用 @JsonProperty 指定映射

修改 BasePagePageWithSearch

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;// ... 其他代码 ...
}
  • 效果
    • 前端用 pageNumpageSizesearchField,后端正确映射。
    • 输入:
      {"searchField": "name","pageNum": 0,"pageSize": 10
      }
      
      • pageWithSearch.getPage()0
      • pageWithSearch.getPageSize()10
方案 2:全局命名策略

application.yml 中配置:

spring:jackson:property-naming-strategy: SNAKE_CASE
  • 效果
    • 前端用 page_numpage_size,自动映射到 pagesize
    • 输入:
      {"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);}
}
  • 效果
    • pagesizenull 时,用默认值 0 和 10。
Mermaid 流程图:修复过程
前端: pageNum=0, pageSize=10
方案 1: @JsonProperty
page=0, size=10
PageRequest.of(0, 10)
正常返回数据
方案 3: toPageableWithDefault
page=0, size=10

🌈 第五幕:经验与启发

学到了啥?💡

  1. Jackson 的严格匹配
    • 字段名不一致,后端字段变 null,小心下游逻辑!
  2. 命名约定很重要
    • 前后端要统一字段名,或者用工具桥接差异。
  3. 防御性编程
    • null 是隐患,提前处理是王道。

小建议 🌟

  • 文档化
    • 用 Swagger 或 API 文档明确字段名,减少误解。
  • 日志排查
    log.info("Received: page={}, size={}", pageWithSearch.getPage(), pageWithSearch.getPageSize());
    

🎬 尾声

500 错误的迷雾到揭开字段名不一致的真相,这场 debug 让我对 JSON 映射有了新认识。希望这篇博客能帮你在前后端对接时少踩坑!有问题欢迎留言,咱们一起聊技术!✌️

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/898328.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

python中使用单例模式在整个程序中只创建一个数据库连接,节省资源

示例代码&#xff1a; from loguru import logger from pymongo import MongoClient from pymongo.errors import ConnectionFailurefrom llm_engineering.settings import settingsclass MongoDatabaseConnector:_instance: MongoClient | None Nonedef __new__(cls, *args,…

AI小白的第六天:必要的数学知识(一)

在学习的过程中&#xff0c;不管是上代码还是理论学习&#xff0c;其中都掺杂了一些数学知识。俗话说“磨刀不误砍柴工”&#xff0c;而我已经“误了砍柴功”了&#xff0c;现在变成了“亡羊补牢&#xff0c;为时不晚”。 线性代数 线性代数是数学的一个分支&#xff0c;主要…

【Linux】Bash是什么?怎么使用?

李升伟 整理 什么是 Bash&#xff1f; Bash&#xff08;Bourne Again Shell&#xff09;是一种 命令行解释器&#xff08;Shell&#xff09;&#xff0c;广泛用于 Unix 和 Linux 操作系统。它是 Bourne Shell&#xff08;sh&#xff09; 的增强版&#xff0c;提供了更多的功能…

Qt Creator入门

1.创建项目 选择创建项目-Application&#xff08;Qt&#xff09;-Qt Widgets Application-修改名称即可 默认创建有窗口类&#xff0c;myWidget,基类有三种选择&#xff1a;QWidget&#xff0c;QMainWindow&#xff0c;QDialog 注意&#xff1a; 名称和创建路径不能有中文、…

C语言经典代码练习题

1.输入一个4位数&#xff1a;输出这个输的个位 十位 百位 千位 #include <stdio.h> int main(int argc, char const *argv[]) {int a;printf("输入一个&#xff14;位数&#xff1a;");scanf("%d",&a);printf("个位&#xff1a;%d\n"…

cls(**dict(data, id=id))灵活地从一个字典生成实例,同时确保某些关键字段(如 id)被正确设置或覆盖

示例代码&#xff1a; classmethoddef from_mongo(cls: Type[T], data: dict) -> T:"""Convert "_id" (str object) into "id" (UUID object)."""if not data:raise ValueError("Data is empty.")id data.pop…

MyBatis XMLMapperBuilder 是如何将 SQL 语句解析成可执行的对象? 如何将结果映射规则解析成对应的处理器?

1. XMLMapperBuilder 如何将 SQL 语句解析成可执行对象 (MappedStatement): XMLMapperBuilder 解析 <select>, <insert>, <update>, <delete> 等 SQL 语句元素时&#xff0c;并不仅仅是简单地读取 SQL 文本&#xff0c;而是要将 SQL 语句和相关的配置…

咖啡点单小程序毕业设计(JAVA+SpringBoot+微信小程序+完整源码+论文)

✌全网粉丝20W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ &#x1f345;文末获取项目下载方式&#x1f345; 一、项目背景介绍&#xff1a; 随着社会的快速发展和…

003-掌控命令行-CLI11-C++开源库108杰

首选的现代C风格命令行参数解析器! &#xff08;本课程包含两段教学视频。&#xff09; 以文件对象监控程序为实例&#xff0c;五分钟实现从命令行读入多个监控目标路径&#xff1b;区分两大时机&#xff0c;学习 CLI11 构建与解析参数两大场景下的异常处理&#xff1b;区分三…

【leetcode hot 100 124】二叉树中的最大路径和

解法一&#xff1a;&#xff08;递归&#xff09;考虑实现一个简化的函数 maxGain(node)&#xff0c;该函数计算二叉树中的一个节点的最大贡献值&#xff0c;具体而言&#xff0c;就是在以该节点为根节点的子树中寻找以该节点为起点的一条路径&#xff0c;使得该路径上的节点值…

谱分析方法

前言 本文隶属于专栏《机器学习数学通关指南》&#xff0c;该专栏为笔者原创&#xff0c;引用请注明来源&#xff0c;不足和错误之处请在评论区帮忙指出&#xff0c;谢谢&#xff01; 本专栏目录结构和参考文献请见《机器学习数学通关指南》 ima 知识库 知识库广场搜索&#…

在图像/视频中裁剪出人脸区域

1. 在图像中裁剪人脸区域 import face_alignment import skimage.io import numpy from argparse import ArgumentParser from skimage import img_as_ubyte from skimage.transform import resize from tqdm import tqdm import os import numpy as np import warnings warni…

【软考-架构】11.3、设计模式-新

✨资料&文章更新✨ GitHub地址&#xff1a;https://github.com/tyronczt/system_architect 文章目录 项目中的应用设计模式创建型设计模式结构型设计模式行为型设计模式 &#x1f4af;考试真题题外话 项目中的应用 在实际项目中&#xff0c;我应用过多种设计模式来解决不同…

使用Redis如何实现分布式锁?(超卖)

分布式锁概念 在多线程环境下&#xff0c;为了保证数据的线程安全&#xff0c;锁保证同一时刻&#xff0c;只有一个可以访问和更新共享数据。在单机系统我们可以使用 synchronized 锁、Lock 锁保证线程安全。 synchronized 锁是 Java 提供的一种内置锁&#xff0c;在单个 JVM …

Linux的Shell编程

一、什么是Shell 1、为什么要学习Shell Linux运维工程师在进行服务器集群管理时&#xff0c;需要编写Shell程序来进行服务器管理。 对于JavaEE和Python程序员来说&#xff0c;工作的需要。Boss会要求你编写一些Shell脚本进行程序或者是服务器的维护&#xff0c;比如编写一个…

使用React和google gemini api 打造一个google gemini应用

实现一个简单的聊天应用&#xff0c;用户可以通过输入问题或点击“Surprise me”按钮获取随机问题&#xff0c;并从后端API获取回答。 import { useState } from "react"; function App() {const [ value, setValue] useState(""); // 存储用户输入的问题…

深入探讨TK矩阵系统:创新的TikTok运营工具

TK矩阵的应用场景 TK矩阵系统适用于多个场景&#xff0c;尤其是在以下几个方面有显著优势&#xff1a; 批量账号管理与内容发布&#xff1a;对于需要管理多个TikTok账号的内容创作者或营销人员&#xff0c;TK矩阵提供了高效的账号管理工具&#xff0c;支持批量发布视频、评论、…

MTK Android12 应用在最顶端时,禁止拉起其他某个应用(一)

1、需求 近期&#xff0c;客户要求应用在最顶端时&#xff0c;禁止拉起其他某个应用2、解决方法 diff --git a/frameworks/base/services/core/java/com/android/server/wm/ActivityStarter.java b/frameworks/base/services/core/java/com/android/server/wm/ActivityStarte…

论文阅读笔记:Deep Unsupervised Learning using Nonequilibrium Thermodynamics

1、来源 论文连接1&#xff1a;http://ganguli-gang.stanford.edu/pdf/DeepUnsupDiffusion.pdf 论文连接2(带appendix)&#xff1a;https://arxiv.org/pdf/1503.03585v7 代码链接&#xff1a;https://github.com/Sohl-Dickstein/Diffusion-Probabilistic-Models 代码的环境配置…

7种数据结构

7种数据结构 顺序表sqlite.hseqlite.c 单链表linklist.clinklist.h 双链表doulinklist.cdoulinklist.h 链式栈linkstack.clinkstack.h 队列SeqQueue.cSeqQueue.h 树tree.c 哈希表hash.c 顺序表 sqlite.h #ifndef __SEQLIST_H__ #define __SEQLIST_H__ typedef struct person…