前后端数据传输格式(下)

news2024/11/19 2:40:50
作者简介:大家好,我是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 {

    @Bean
    public 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

进群,大家一起学习,一起进步,一起对抗互联网寒冬

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

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

相关文章

解释AI决策,这10个强大的 Python 库记得收藏!

本文整理了10个常用于可解释AI的Python库&#xff0c;方便我们更好的理解AI模型的决策。 什么是XAI&#xff1f; XAI&#xff08;Explainable AI&#xff09;的目标是为模型的行为和决策提供合理的解释&#xff0c;这有助于增加信任、提供问责制和模型决策的透明度。XAI 不仅…

鸿蒙生态开发就业前景到底好不好

鸿蒙生态开发是指基于华为自主研发的操作系统鸿蒙&#xff08;HarmonyOS&#xff09;进行应用程序开发和生态建设。目前&#xff0c;鸿蒙生态开发的前景非常好&#xff0c;原因如下&#xff1a;做鸿蒙应用开发到底学习些啥&#xff1f; (qq.com) 1&#xff1a;政府支持&#x…

高德地图vue实现自定义标点热力图效果(缩放时展示不同数据)

高德地图插件引入省略。。。样式和vue基础组件省略。。。 如果每个标点没有数值&#xff0c;则可以用点聚合来实现功能下面例子&#xff0c;每个标点会有按市统计的数值&#xff0c;而且缩放一定程度时&#xff0c;需要展示按省统计的标点&#xff0c;因此需要自定义标点样式和…

Ubuntu中编译出Windows的可执行程序(.exe)

1、前言 在嵌入式开发中&#xff0c;交叉编译是很常见的情况&#xff0c;如果你把Windows电脑也看做一块高性能的开发板&#xff0c;那在Ubuntu中编译出Windows上运行的可执行程序也是很好理解的行为。 2、安装mingw64环境 sudo apt-get install mingw-w64 3、测试编译链是否安…

特权FPGA 学习笔记

存储器可用于异步时钟域的信号处理&#xff0c;双口RAM多用于交互式数据&#xff0c;FIFO多用于单向数据传输&#xff1b;以task的方式封装testbench子程序&#xff0c;以提高复用程度&#xff1b;模板中&#xff0c;vho是vhdl模板&#xff0c;veo是verilog模板&#xff1b;run…

第七届中老越三国丢包狂欢节暨2023年中老越三国(普洱)边境商品交易会新闻发布会在昆明召开

12月8日&#xff0c;第七届中老越三国丢包狂欢节暨2023年中老越三国&#xff08;普洱&#xff09;边境商品交易会新闻发布会在昆明召开。据悉&#xff0c;本届丢包节暨边交会将于2023年12月22日至26日在普洱市江城哈尼族彝族自治县举办。 发布会现场 中老越三国丢包狂欢节自200…

解决 php 连接mysql数据库时报错:Fatal error: Class ‘mysqli’ not found in问题

在使用php对mysql进行连接的过程中&#xff0c;出现了Fatal error: Uncaught Error: Class "mysqli" not found in的问题 解决方案 这个错误通常表示您的PHP代码中缺少MySQL扩展或者没有启用MySQL扩展。 我们首先确认一下PHP环境中已经安装了MySQL扩展。检查一下自己…

v4l2接收流程

内核media驱动目录结构 目录media/driver,子目录说明如下&#xff0c;主要列举本文中使用到的目录 目录功能I2C摄像头&#xff0c;解串器&#xff08;max9296/9295等&#xff09;platform控制器的驱动&#xff0c;例如mipi控制等v4l2_coreioctl 入口等media\common\videobuf2…

哈希表的几种实现方式与比较

版权声明 本文原创作者&#xff1a;谷哥的小弟作者博客地址&#xff1a;http://blog.csdn.net/lfdfhl 哈希表概述 哈希表&#xff08;Hash Table&#xff09;是一种常用的数据结构&#xff0c;用于实现键值对的映射关系。它通过哈希函数将键映射到一个特定的索引位置&#xf…

Spring Boot 3 整合 Mybatis-Plus 实现动态数据源切换实战

&#x1f680; 作者主页&#xff1a; 有来技术 &#x1f525; 开源项目&#xff1a; youlai-mall &#x1f343; vue3-element-admin &#x1f343; youlai-boot &#x1f33a; 仓库主页&#xff1a; Gitee &#x1f4ab; Github &#x1f4ab; GitCode &#x1f496; 欢迎点赞…

银河麒麟本地软件源配置方法

软件源介绍 软件源可以理解为软件仓库&#xff0c;当需要安装软件时则会根据源配置去相应的软件源下载软件包&#xff0c;此方法的优点是可以自动解决软件包的依赖关系。常见的软件源有光盘源、硬盘源、FTP源、HTTP源&#xff0c;本文档主要介绍本地软件源的配置方法&#xff…

专注抖音短视频账号矩阵系统源头开发---saas工具

抖音账号|短视频矩阵分发系统 | 多账号管理发布 |MVC架 短视频矩阵分发系统是一种可以帮助企业、机构和个人高效分发短视频的工具。随着社交媒体的不断普及&#xff0c;短视频的使用越来越广泛&#xff0c;因此如何快速而准确地将短视频传播到不同的平台和账号上已经成为了一个…

短剧分销平台搭建:短剧变现新模式

短剧作为今年大热的行业&#xff0c;深受大众追捧&#xff01;短剧剧情紧凑&#xff0c;几乎每一集都有高潮剧情&#xff0c;精准击中了当下网友的碎片化时间。 短剧的形式较为灵活&#xff0c;可以轻松融入各种的元素&#xff0c;比如喜剧、悬疑、爱情等&#xff0c;可以满足…

一加 12 Pop-up快闪活动来袭,十城联动火爆开启

12 月 9 日&#xff0c;一加 12 Pop-up 快闪活动在北京、深圳、上海、广州等十城联动开启&#xff0c;各地加油欢聚快闪现场&#xff0c;抢先体验与购买一加 12。作为一加十年超越之作&#xff0c;一加 12 全球首发拥有医疗级护眼方案和行业第一 4500nit 峰值亮度的 2K 东方屏、…

postman常用脚本

一、在参数中动态添加开始时间和结束时间的时间戳 1.先在collection中添加参数&#xff0c;这里的作用域是collection&#xff0c;也可以是其他的任何scope 2.在Pre-request Script 中设定开始时间和结束时间参数&#xff0c;比如昨天和今天的时间戳&#xff0c;下面是js代码 …

彻底搞懂零拷贝技术( DMA、PageCache)

DMA 直接内存访问&#xff08;Direct Memory Access&#xff09; 什么是DMA&#xff1f; 在进行数据传输的时候&#xff0c;数据搬运的工作全部交给 DMA 控制器&#xff0c;而 CPU 不再参与&#xff0c;可以去干别的事情。 传统I/O 在没有 DMA 技术前&#xff0c;全程数据…

【图论笔记】克鲁斯卡尔算法(Kruskal)求最小生成树

【图论笔记】克鲁斯卡尔算法&#xff08;Kruskal&#xff09;求最小生成树 适用于 克鲁斯卡尔适合用来求边比较稀疏的图的最小生成树 简记&#xff1a; 将边按照升序排序&#xff0c;选取n-1条边&#xff0c;连通n个顶点。 添加一条边的时候&#xff0c;如何判断能不能添加…

链表OJ—相交链表

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言 1、相交链表的题目&#xff1a; 方法讲解&#xff1a; 图文解析&#xff1a; 代码实现&#xff1a; 总结 前言 世上有两种耀眼的光芒&#xff0c;一种是正在升…

《PySpark大数据分析实战》图书上线啦

《PySpark大数据分析实战》图书上线啦 《PySpark大数据分析实战》图书上线啦特殊的日子关于创作关于数据关于Spark关于PySpark关于图书/专栏 《PySpark大数据分析实战》图书上线啦 特殊的日子 不知不觉一转眼入驻CSDN已经满一年了&#xff0c;这真是一个充满意义的特殊的日子&…

SystemUI下拉通知菜单栏定时自动隐藏

前言 在系统应用开发过程中&#xff0c;常常遇到一些特殊的需求&#xff0c;Android原生的应用并无此适配&#xff0c;此时需要对系统应用进行定制化开发。 目前遇到的这样一个需求&#xff1a;下拉通知菜单栏时&#xff0c;定时8秒后自动关闭通知菜单栏。通知菜单栏为Sytstem…