前后端数据传输格式(下)
作者简介:大家好,我是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.简…...

(LeetCode 每日一题) 3442. 奇偶频次间的最大差值 I (哈希、字符串)
题目:3442. 奇偶频次间的最大差值 I 思路 :哈希,时间复杂度0(n)。 用哈希表来记录每个字符串中字符的分布情况,哈希表这里用数组即可实现。 C版本: class Solution { public:int maxDifference(string s) {int a[26]…...

地震勘探——干扰波识别、井中地震时距曲线特点
目录 干扰波识别反射波地震勘探的干扰波 井中地震时距曲线特点 干扰波识别 有效波:可以用来解决所提出的地质任务的波;干扰波:所有妨碍辨认、追踪有效波的其他波。 地震勘探中,有效波和干扰波是相对的。例如,在反射波…...
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以?
Golang 面试经典题:map 的 key 可以是什么类型?哪些不可以? 在 Golang 的面试中,map 类型的使用是一个常见的考点,其中对 key 类型的合法性 是一道常被提及的基础却很容易被忽视的问题。本文将带你深入理解 Golang 中…...
STM32+rt-thread判断是否联网
一、根据NETDEV_FLAG_INTERNET_UP位判断 static bool is_conncected(void) {struct netdev *dev RT_NULL;dev netdev_get_first_by_flags(NETDEV_FLAG_INTERNET_UP);if (dev RT_NULL){printf("wait netdev internet up...");return false;}else{printf("loc…...
Go 语言接口详解
Go 语言接口详解 核心概念 接口定义 在 Go 语言中,接口是一种抽象类型,它定义了一组方法的集合: // 定义接口 type Shape interface {Area() float64Perimeter() float64 } 接口实现 Go 接口的实现是隐式的: // 矩形结构体…...
spring:实例工厂方法获取bean
spring处理使用静态工厂方法获取bean实例,也可以通过实例工厂方法获取bean实例。 实例工厂方法步骤如下: 定义实例工厂类(Java代码),定义实例工厂(xml),定义调用实例工厂ÿ…...

学习STC51单片机31(芯片为STC89C52RCRC)OLED显示屏1
每日一言 生活的美好,总是藏在那些你咬牙坚持的日子里。 硬件:OLED 以后要用到OLED的时候找到这个文件 OLED的设备地址 SSD1306"SSD" 是品牌缩写,"1306" 是产品编号。 驱动 OLED 屏幕的 IIC 总线数据传输格式 示意图 …...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...

vue3+vite项目中使用.env文件环境变量方法
vue3vite项目中使用.env文件环境变量方法 .env文件作用命名规则常用的配置项示例使用方法注意事项在vite.config.js文件中读取环境变量方法 .env文件作用 .env 文件用于定义环境变量,这些变量可以在项目中通过 import.meta.env 进行访问。Vite 会自动加载这些环境变…...
稳定币的深度剖析与展望
一、引言 在当今数字化浪潮席卷全球的时代,加密货币作为一种新兴的金融现象,正以前所未有的速度改变着我们对传统货币和金融体系的认知。然而,加密货币市场的高度波动性却成为了其广泛应用和普及的一大障碍。在这样的背景下,稳定…...