前后端数据传输格式(下)
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬
上篇主要复习了HTTP以及POST相关的几种传参形式,这一篇来讲讲和实际开发更为紧密的内容:JSON。
初学者在需求分析阶段通常缺乏以下能力:
- 无法通过看页面原型图分析出大致的请求格式和响应格式
- 即使能分析出请求格式,却不知道用什么样的Java对象接收
- 即使能分析出响应格式,却不知道用什么样的Java对象返回
这三个能力,其实都依赖于你对JSON的理解(本文主要讨论JSON,不涉及表单请求和文件上传类型)。
说得更具体些就是,初学者往往搞不清楚JSON与Java对象的转换关系(图片来自尚硅谷):
- 请求:JSON转Java
- 响应:Java转JSON
JSON
JSON的作用
在我刚入行时写过相关的博客,比较简单,感兴趣的同学可以看看:AJAX与JSON
JSON简单来说就是特定格式的字符串,注意,它的载体是字符串。很多人分不清JSON和JS对象,其实两者没有可比性。实在要说的话,JS对象和Java对象都是正儿八经的对象,存活于内存中(浏览器/服务器),而JSON只是字符串,往往承担的是网络传输的角色:
我们都知道,网页上的动态数据都是服务器那边组装后通过HTTP返回的,但很少有人会思考这样的一个问题:
服务器组装数据时用的是Java对象,比如user.setName("bravo"),但前端得到的却是JS对象,怎么做到的?
一般来说,浏览器被安装在我们的个人电脑中,而提供服务的Java应用则可能运行在阿里云服务器上,两者并不在同一片物理内存。由于对象存活必须依赖于内存(如果不做持久化,只要一断电,内存中的数据就没了),所以就上图而言,Java对象显然没法自己“跳出”服务器进入浏览器中,更别提Java对象如何变成JS对象。
要想完成跨内存的数据传输、对象转换,必须通过网络传输,而且需要一个传递信息的载体,过程中还涉及到序列化和反序列化。
实际上,整个请求响应的过程就是序列化和反序列化的过程:
- 服务器端把Java对象序列化为JSON(中介,特定格式的字符串)
- 网络不能直接传递对象,但可以传输字符串
- 浏览器得到JSON后,反序列化为JS对象,然后设置到页面上
同理,不仅是浏览器和服务器,服务器和服务器之间也需要JSON作为数据交互的中介:
JSON的格式
使用Postman时,我们经常会发这样的请求:
Body就是请求体内容,一般都是字符串格式,所以这是一个JSON,而不是JS对象,尽管它们看起来很像。你可以理解为JSON是多种语言共同协定的一种数据交互格式:
- "field":"xxx" 表示对象的字段,value如果是字符类型需要加"",数值可以不加
- {} 表示对象或map或其他符合key-value格式的结构
- [] 表示一组对象、一组字符串或一组数值
很简单吧?
然后各个语言都会遵守这个协定,转化为自己的对象结构,比如:
- {}可以代表Java对象/Map,[]由于表示一组数据,刚好可以对应Java的数组、List或Set等单列集合
- {}也可以代表Python对象/字典,[]对应Python的元组或list等
- {}还可以代表PHP的对象,[]对应PHP中的Array
- ...
当然,{}也可以代表JS对象,[]则可以转化为JS的数组。
为什么很多初学者会搞混JSON和JS对象呢?本质上还是因为:
- 初学者都“见过”JS对象,而且它往往都是{}的形式出现
- JS对象的格式和JSON确实比较像
但JSON本质是“干瘪瘪”的字符串,当各大语言需要进行反序列化时,就会按照上面的格式转为内存中“圆鼓鼓”的对象。应该把JS对象和Java对象看作一个梯队,而JSON则在另一个梯队,是一个特定格式的字符串。
常见JSON格式与Java对象的转换
这里只演示Java对象与JSON的转换:
如果不信的话,可以自己动手写一下接口,然后用Postman按照图中的格式发送JSON,看看接口能不能顺利接收参数。
上面说过,JSON的{}可以对应Java的对象或者Map,{}两个括号表示对象的边界,其实刚好对应类的{},里面的就是对象的字段。
我们来分析一下第一张图的结构。
接口返回的是一个HashMap,所以很容易想到最终JSON格式是:
{
...
}
接着往Map里put了一些value,我们先不管value是什么类型:
{
"1号男嘉宾" : xxx,
"2号男嘉宾" : xxx,
"3号男嘉宾" : xxx,
}
OK,JSON的大致结构出来了,再深入一层,看看xxx是什么。很明显,是一个Java的User对象,还是对应{}:
{
"1号男嘉宾" : {},
"2号男嘉宾" : {},
"3号男嘉宾" : {},
}
那么,这个User对象有哪些字段呢?填上即可:
{"1号男嘉宾": {"name": "雅木茶","age": 23},"2号男嘉宾": {"name": "卡卡罗特","age": 23},"3号男嘉宾": {"name": "贝吉塔","age": 22}
}
其他两个分析过程同理,就不演示了(思考一下,如果User里面有Department会是什么样)。
刚才是顺着来,现在我们玩一下“逆推”。假设现在前端跑过来告诉你
大佬,这个接口我到时候这样传参给你行吗?
[{"name": "张飞","age": 18,"tags": ["大眼睛","大胡子"]},{"name": "关羽","age": 19,"tags": ["万人敌","长胡子"]},{"name": "刘备","age": 20,"tags": ["刘皇叔","摔阿斗"]}
]
此时你应该如何设计入参才能接受前端这种格式的JSON呢?
jackson的一些操作
之前介绍过,服务器本身没有能力处理JSON和文件上传,都要靠第三方组件。SpringBoot则引入了jackson作为默认的JSON组件,其他常见的还有阿里的fastJson和谷歌的gson。
这里介绍一下jackson常见的几个注解。
@Slf4j
@RestController
public class UserController {@PostMapping("/addUser")public UserPOJO addUser(@RequestBody UserPOJO user) {user.setAge(null);return user;}}@Data
public class UserPOJO {/*** 姓名*/private String name;/*** 年龄*/private Integer age;/*** 用户类型*/private Integer userType;/*** 个人标签*/private List<String> tags;}
@JsonInclude
有时我们希望如果字段为null就不要返回给前端,可以使用@JsonInclude,它可以指定很多属性。
@JsonInclude还可以加在类上,那么该对象所有为null的字段都不会参与JSON序列化。
@JsonProperty
对于一些老项目或者其他什么原因,原本传参使用的是下划线,比如user_type,而后端用Java改写时又要符合驼峰命名,此时可以用@JsonProperty做一层“隔离”。
此时出入参都必须叫user_type:
@JsonFormat
有些同学容易把@JsonFormat和@DateTimeFormat搞混,我们单独开一个小节聊一聊。
时间格式
首先和大家说一下,数据库字段无论是datetime还是timestamp,其实都是可以自动对应Java的Date对象,一般讨论的所谓时间格式,都是指前端的显示格式:
刚才我们讨论为什么需要JSON时,提到一个观点:对象是在内存中存活的,无法直接进行网络传输。但是大家有没有想过:
class User {private String name;private Date birthday;
}
其实里面的字段也是对象,也要进行序列化。SpringBoot引入了jackson作为JSON序列化的组件,其中必然包括对Date进行序列化/反序列化的方案。
然而,SpringBoot1.x和2.x其实有较大的改动,其中就包括对Date格式化的改动。大家可以沿用刚才的项目,给UserPOJO加上birthday字段,然后在SpringBoot1.5.9和SpringBoot2.3.4环境下实验。
@Slf4j
@RestController
public class UserController {@PostMapping("/addUser")public UserPOJO addUser(@RequestBody UserPOJO user) {return user;}}
SpringBoot1.x
SpringBoot2.x
有两个细节:
- SpringBoot1.x的返回值是毫秒数,SpringBoot2.x是另一种格式
- 当入参和出参时间格式不同时,会发生转换,此时会出现时间差
-
- SpringBoot1.x传递2020-12-07T22:58:11.000+00:00,返回1607381891000(+8)
- SpringBoot2.x传递1607353091000,返回2020-12-07T14:58:11.000+00:00(-8)
可以通过时间戳转换验证一下(注意单位)。
时差的问题可以通过配置解决,比如:
spring.jackson.time-zone=GMT+8
总的来说就是:
如果希望更改出入参的时间格式,可以有局部和全局两种方式:
- 局部:@JsonFormat / @DateTimeFormat
- 全局
-
- YAML
- Config
@DateTimeFormat只适用于非JSON的POST请求,也就是说,如果项目本身都是JSON请求,你用@DateTimeFormat是无效的。你可以简单理解为:
- @DateTimeFormat,走表单请求时间转换器(适用于GET、POST表单请求)
- @JsonFormat,走JSON请求时间转换器(适用于POST JSON请求、JSON响应)
所以,你在这煞费苦心地调整表单请求的转换格式有啥用?
这里演示一下@JsonFormat的用法:
/*** 生日(时间格式很容易写错,可以抽取为常量或者使用第三方提供的,比如hutool就有)*/
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private Date birthday;
此时出入参都变为指定格式:
也可以在YAML中进行全局配置:
上面那个mvc:date-format是对表单请求的配置。
或者使用Config:
@Configuration
public class JacksonConfig {@Beanpublic ObjectMapper getObjectMapper() {ObjectMapper objectMapper = new ObjectMapper();// 全局配置objectMapper.setSerializationInclusion(JsonInclude.Include.NON_NULL);objectMapper.setDateFormat(new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"));return objectMapper;}}
如果说@JsonInclude/@JsonFormat加到字段上、类上分别是字段级别、类级别,那么上面的配置就是整个项目级别,因为Spring的bean默认单例,而这个唯一的ObjectMapper已经被做了手脚,最终所有接口的序列化/反序列化行为都被改写。
这种全局和局部的思想,后面会很常见,这里先点一下。
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO
进群,大家一起学习,一起进步,一起对抗互联网寒冬
相关文章:

前后端数据传输格式(下)
作者简介:大家好,我是smart哥,前中兴通讯、美团架构师,现某互联网公司CTO 联系qq:184480602,加我进群,大家一起学习,一起进步,一起对抗互联网寒冬 上篇主要复习了HTTP以及…...

mysql pxc高可用离线部署(三)
pxc学习流程 mysql pxc高可用 单主机 多主机部署(一) mysql pxc 高可用多主机离线部署(二) mysql pxc高可用离线部署(三) mysql pxc高可用 跨主机部署pxc 本文使用docker进行安装,主机间通过…...
XXL-JOB 日志表和日志文件自动清理
🚀 作者主页: 有来技术 🔥 开源项目: youlai-mall 🍃 vue3-element-admin 🍃 youlai-boot 🌺 仓库主页: Gitee 💫 Github 💫 GitCode 💖 欢迎点赞…...

常用sql记录
备份一张表 PostgreSQL CREATE TABLE new_table AS SELECT * FROM old_table;-- 下面这个比上面好,这个复制表结构时,会把默认值、约束、注释都复制 CREATE TABLE new_table (LIKE old_table INCLUDING ALL) WITHOUT OIDS; INSERT INTO new_table SELE…...

设备温度和振动综合监测:温振一体式传感器的优点和应用
随着工业设备的复杂性和自动化程度的提高,对设备状态监测的需求也日益增加。温振一体式传感器作为一种集振动和温度监测于一体的传感器,具备多项优势,因此在工业设备状态监测领域得到广泛应用。 温振一体式传感器基于振动传感器和温度传感器的…...

彻底解决ModuleNotFoundError: No module named ‘exceptions‘【Bug完美解决】
文章目录 项目场景:问题描述原因分析:解决方案:此Bug解决方案总结心得项目场景: 根据本文可找到bug原因并彻底解决**ModuleNotFoundError: No module named ‘exceptions‘**Bug 报错: E:\Anconda\python.exe c:\Users\24190\PycharmProjects\pythonProject4py尝试 gong…...
yarn和npm的区别
2023-12-8 yarn和npm的区别 是常用的包管理工具,用于node.js项目中安装、管理、和更新依赖项 有以下几个区别: 性能和速度:在包的安装和下载方面,yarn比npm更快速,yarn通过并行下载和缓存等优化策略,可以…...
设计图中时序图
设计图中的时序图通常用于展示两个或多个对象之间的交互和消息传递的顺序。它是一种用于描述软件或系统中的并发性和时序行为的工具。 以下是一个简单的时序图的示例: 首先,在时序图中创建两个对象,例如"对象A"和"对象B&quo…...

反射实现tomcat
获取类信息的方法 1.通过类对象 x.getClass() 2.通过class.forname方法 Class.forname(className);这里className是存储类名的字符串 3.通过类名.class 类名.class 通过类名创建对象 类名.newInstance(); 反射可以看到类的一切信息࿱…...

Ubuntu 安装 CUDA 和 cuDNN 详细步骤
我的Linux系统背景: 系统和驱动都已安装。 系统是centos 8。查看自己操作系统的版本信息:cat /etc/issue或者是 cat /etc/lsb-release 用nvidia-smi可以看到显卡驱动和可支持的最高cuda版本,我的是12.2。驱动版本是535.129.03 首先&#…...

ArkTS快速入门
一、概述 ArkTS是鸿蒙生态的应用开发语言。它在保持TypeScript(简称TS)基本语法风格的基础上,对TS的动态类型特性施加更严格的约束,引入静态类型。同时,提供了声明式UI、状态管理等相应的能力,让开发者可以…...
HTTP不同场景下的通信过程和用户上网认证过程分析
目录 HTTP不同场景的通信过程 HTTP正常交互过程 HTTP透明加速传输过程 HTTP代理服务器场景下交互过程 通过AC对上网用户不同场景的认证过程 AC上网认证正常交互过程 通过Cookie实现免认证交互过程 代理服务器场景下HTTP密码认证交互过程 HTTP不同场景的通信过程 HTTP、…...

VR 实现 Splash Screen 效果
文章目录 背景官方实现逆向分析 背景 手机 App 在实现 Splash Screen 的时候,目前都有成熟的方案可以参考,但是在做 VR 开发时,要如何实现一个 App 自己的 Splash Screen ,下面是我们基于 PICO & OCULUS 进行业务开发时经过探…...

HarmonyOS学习--TypeScript语言学习(一)
注意:这只是我学习的笔记!!! 注意:这只是我学习的笔记!!! 注意:这只是我学习的笔记!!! 本章目录如下: 一、TypeScript语言…...

【C语言】函数递归详解(一)
目录 1.什么是递归: 1.1递归的思想: 1.2递归的限制条件: 2.递归举例: 2.1举例1:求n的阶乘: 2.1.1 分析和代码实现: 2.1.2图示递归过程: 2.2举例2:顺序打印一个整数的…...
WT588F02B-8S语音芯片助力破壁机:智能声音播放提示IC引领健康生活新潮流
在追求健康饮食的时代潮流中,破壁机作为榨汁、搅拌的重要厨房电器,融入智能技术的趋势不断加强。唯创知音的WT588F02B-8S语音芯片作为声音播放提示IC,为破壁机注入了更智能、便捷的声音提示功能,引领用户迈入健康生活的新潮流。 …...

NXP iMX8M Plus Qt5 双屏显示
By Toradex胡珊逢 简介 双屏显示在显示设备中有着广泛的应用,可以面向不同群体展示特定内容。文章接下来将使用 Verdin iMX8M Plus 的 Arm 计算机模块演示如何方便地在 Toradex 的 Linux BSP 上实现在两个屏幕上显示独立的 Qt 应用。 硬件介绍 Verdin iMX8M Plu…...
RepidJson中Writer类、FilewriteStream类、 PrettyWriter类的区别
rapidjson是一个C的JSON解析库,可以用于解析和序列化JSON数据。 Writer是rapidjson中一种基本的输出流,用于将JSON数据输出到字符串或文件中。 FileWriteStream是一个Writer的子类,它专门用于将JSON数据输出到文件中。相比于普通的Writer&a…...

IntelliJ idea卡顿解决,我遇到的比较管用的方案
Setttings> Build, Execution,Deployment>Debugger> Data Views> Java 取消 Enable "toString()" object view; Speed up debugging in IntelliJ Yesterday, I observed painfully slow debugging in IntelliJ. Every step over or step in took almost…...
Fabric.js 实战开发使用介绍
原生canvas用的多的有哪些槽点就不用我多说了;fabric 作为一个canvas库,提供了非常高效、直观的API操作,使我们对涉及canvas相关的功能开发效率大幅提升~~~~ 简单记录下自己的心得;以下是对比canvas来说的优势: 1.简…...

DAY 47
三、通道注意力 3.1 通道注意力的定义 # 新增:通道注意力模块(SE模块) class ChannelAttention(nn.Module):"""通道注意力模块(Squeeze-and-Excitation)"""def __init__(self, in_channels, reduction_rat…...
MVC 数据库
MVC 数据库 引言 在软件开发领域,Model-View-Controller(MVC)是一种流行的软件架构模式,它将应用程序分为三个核心组件:模型(Model)、视图(View)和控制器(Controller)。这种模式有助于提高代码的可维护性和可扩展性。本文将深入探讨MVC架构与数据库之间的关系,以…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

Rust 开发环境搭建
环境搭建 1、开发工具RustRover 或者vs code 2、Cygwin64 安装 https://cygwin.com/install.html 在工具终端执行: rustup toolchain install stable-x86_64-pc-windows-gnu rustup default stable-x86_64-pc-windows-gnu 2、Hello World fn main() { println…...
LangFlow技术架构分析
🔧 LangFlow 的可视化技术栈 前端节点编辑器 底层框架:基于 (一个现代化的 React 节点绘图库) 功能: 拖拽式构建 LangGraph 状态机 实时连线定义节点依赖关系 可视化调试循环和分支逻辑 与 LangGraph 的深…...
LCTF液晶可调谐滤波器在多光谱相机捕捉无人机目标检测中的作用
中达瑞和自2005年成立以来,一直在光谱成像领域深度钻研和发展,始终致力于研发高性能、高可靠性的光谱成像相机,为科研院校提供更优的产品和服务。在《低空背景下无人机目标的光谱特征研究及目标检测应用》这篇论文中提到中达瑞和 LCTF 作为多…...
SpringAI实战:ChatModel智能对话全解
一、引言:Spring AI 与 Chat Model 的核心价值 🚀 在 Java 生态中集成大模型能力,Spring AI 提供了高效的解决方案 🤖。其中 Chat Model 作为核心交互组件,通过标准化接口简化了与大语言模型(LLM࿰…...
React核心概念:State是什么?如何用useState管理组件自己的数据?
系列回顾: 在上一篇《React入门第一步》中,我们已经成功创建并运行了第一个React项目。我们学会了用Vite初始化项目,并修改了App.jsx组件,让页面显示出我们想要的文字。但是,那个页面是“死”的,它只是静态…...
2.2.2 ASPICE的需求分析
ASPICE的需求分析是汽车软件开发过程中至关重要的一环,它涉及到对需求进行详细分析、验证和确认,以确保软件产品能够满足客户和用户的需求。在ASPICE中,需求分析的关键步骤包括: 需求细化:将从需求收集阶段获得的高层需…...

表单设计器拖拽对象时添加属性
背景:因为项目需要。自写设计器。遇到的坑在此记录 使用的拖拽组件时vuedraggable。下面放上局部示例截图。 坑1。draggable标签在拖拽时可以获取到被拖拽的对象属性定义 要使用 :clone, 而不是clone。我想应该是因为draggable标签比较特。另外在使用**:clone时要将…...