前言
在我们日常开发过程中,接口与接口之间,前后端之间数据的传输都使用 JSON 格式。本文介绍Sping Boot中
一、JSON简介
文章开始之前,我们先简单回顾一下JSON的概念
参考自官方JSON中文说明以及JSON维基百科
(1)名称:
JSON(JavaScript Object Notation,JavaScript对象表示法,读作/ˈdʒeɪsən/)
(2)与XML对比:
JSON是一种轻量级的资料交换语言,而XML是一个完整的标记语言,这使得XML在程序判读上需要比较多的功夫。XML利用标记语言的特性提供了绝佳的延展性(如XPath),在数据存储,扩展及高级检索方面相较JSON更具优势,而JSON则由于比XML更加小巧,以及浏览器的内建快速解析支持,使得其更适用于网络数据传输领域。
(3)语法:
- 数据在名称/值对中:json数据是由键值对构成的, 键用引号(单双都行)引起来,也可以不使用引号
- 数据由逗号分隔:多个键值对由逗号分隔
- 花括号保存对象:使用{}定义json 格式
- 方括号保存数组:[ ]
- 值的取值类型:
1. 数字(整数或浮点数)
2. 字符串(在双引号中)
3. 逻辑值(true 或 false)
4. 数组(在方括号中) {“persons”:[{},{}]}
5. 对象(在花括号中) {“address”:{“province”:“广东”…}}
6. null
(4)JSON解析器:
常见的解析器:Gson(Google的JSON处理框架),fastjson(阿里巴巴的JSON处理框架),jackson(Spring Boot默认JSON处理框架)
二、Spring Boot默认对JSON的处理
(1)创建Spring Boot工程并在pom.xml加入依赖如下
<dependencies>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
<exclusions>
<exclusion>
<groupId>org.junit.vintage</groupId>
<artifactId>junit-vintage-engine</artifactId>
</exclusion>
</exclusions>
</dependency>
</dependencies>
这里我们只需关注spring-boot-starter-web
依赖
在IDEA中,对着spring-boot-starter-web依赖
按住CTRL+鼠标左键
可以看到spring-boot-starter-web
封装了一个spring-boot-starter-json
依赖
我们再点进去,可以看到jackson的各项依赖已经被封装好
(2)创建实体类命名为Person.java
public class Person {
private String name;
private Integer age;
private Integer phoneNumber;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
public Integer getPhoneNumber() {
return phoneNumber;
}
public void setPhoneNumber(Integer phoneNumber) {
this.phoneNumber = phoneNumber;
}
public Person(String name,Integer age,Integer phoneNumber){
this.name = name;
this.age = age;
this.phoneNumber = phoneNumber;
}
}
(3)创建Controller类命名为PersonController.java
import com.rex.springbootjson.entity.Person;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
@RestController
public class PersonController {
@RequestMapping("/person")
public Person getPerson(){
return new Person("张三",18,123);
}
@RequestMapping("/list")
public List<Person> getPersonList() {
List<Person> personList = new ArrayList<>();
Person user1 = new Person("李四",18,123);
Person user2 = new Person("王五",15,321);
personList.add(user1);
personList.add(user2);
return personList;
}
@RequestMapping("/map")
public Map<String, Object> getPersonMap() {
Map<String, Object> map = new HashMap<>(3);
Person person = new Person("王五",24,321);
map.put("成员信息", person);
map.put("住址", null);
map.put("学历", "本科");
return map;
}
}
这里我们重点关注@RestController注解
CTRL+鼠标左键点开@RestController注解,可见其封装了@Controller
注解。而@RestController与@Controller的主要区别是:
1. @RestController是将json/xml返回到前端页面
2. @Controller是将HTML页面返回到前端页面
而另一个注解@ResponseBody的作用简而言之就是将对象转化为JSON格式
具体详解可以参考@ResponseBody详解
(4)启动Spring Boot项目
- 访问http://localhost:8080/person
- 访问http://localhost:8080/list,返回List类型的JSON
3.访问http://localhost:8080/map ,返回map类型的JSON
三、 jackson 中对null的处理
由上图,地址一栏出现了null值,实际项目开发中难免会遇到一些 null 值出现,我们转 json 时,是不希望有这些 null 出现的,比如我们期望所有的 null 在转 json 时都变成 “” 这种空字符串,此时我们需要新建一个JSON配置类
- 创建JsonConfig.java文件,加入如下代码
import com.fasterxml.jackson.core.JsonGenerator;
import com.fasterxml.jackson.databind.JsonSerializer;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializerProvider;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
import java.io.IOException;
@Configuration
public class JsonConfig {
@Bean
@Primary
@ConditionalOnMissingBean(ObjectMapper.class)
public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
ObjectMapper objectMapper = builder.createXmlMapper(false).build();
objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
@Override
public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException {
jsonGenerator.writeString("");
}
});
return objectMapper;
}
}
- 重启Spring Boot项目,重新访问http://localhost:8080/map
可以看到null值已经被处理
四、统一数据返回格式
1.为什么要做统一数据返回格式
在以前后端分离为主流的开发环境下,前后端开发人员需要通过API交互,但为了前端人员能够快速做逻辑展示与页面交互处理,每一次RESTful请求都应该包含以下几个信息:
名称 | 描述 |
---|---|
状态码 | 标识请求成功与否,如[1:成功; -1:失败] |
错误码 | 定义明确错误码,更好的应对业务异常;请求成功该值可为空 |
错误消息 | 与错误码相对应,更具体的描述异常信息 |
resultBody | 通常是Bean对象对应的JSON数据,通常为了应对不同返回值类型,将其声明为泛型类型 |
这里总结下我们常用的状态码以及表示的含义:
code区间 | 类型 | 含义 |
---|---|---|
1** | 100-199 | 信息 服务器接收到请求,需要请求者继续执行操作 |
2** | 200-299 | 成功 请求被成功接收并处理 |
3** | 300-399 | 重定向 需要进一步的操作以完成请求 |
4** | 400-499 | 客户端错误 请求包含语法错误或无法完成请求 |
5** | 500-599 | 服务器错误 服务器在处理的时候发生错误 |
2.统一的JSON结构的配置
在config文件夹下创建UnifiedReturnConfig文件
package com.rex.springbootjson.config;
import com.rex.springbootjson.entity.CommonResult;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.config.annotation.EnableWebMvc;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
@EnableWebMvc
@Configuration
public class UnifiedReturnConfig {
@RestControllerAdvice("com.rex.springbootjson.Controller")
static class CommonResultResponseAdvice implements ResponseBodyAdvice<Object> {
@Override
public boolean supports(MethodParameter methodParameter, Class<? extends HttpMessageConverter<?>> aClass) {
return true;
}
@Override
public Object beforeBodyWrite(Object body, MethodParameter methodParameter, MediaType mediaType, Class<? extends HttpMessageConverter<?>> aClass, ServerHttpRequest serverHttpRequest, ServerHttpResponse serverHttpResponse) {
if (body instanceof CommonResult){
return body;
}
return new CommonResult<Object>(body);
}
}
}
在entity文件下创建实体类CommmonResult,添加如下代码
package com.rex.springbootjson.entity;
public final class CommonResult<T> {
private int status = 1;
private String errorCode = "";
private String errorMsg = "";
private T resultBody;
public CommonResult() {
}
public int getStatus() {
return status;
}
public void setStatus(int status) {
this.status = status;
}
public String getErrorCode() {
return errorCode;
}
public void setErrorCode(String errorCode) {
this.errorCode = errorCode;
}
public String getErrorMsg() {
return errorMsg;
}
public void setErrorMsg(String errorMsg) {
this.errorMsg = errorMsg;
}
public T getResultBody() {
return resultBody;
}
public void setResultBody(T resultBody) {
this.resultBody = resultBody;
}
public CommonResult(T resultBody) {
this.resultBody = resultBody;
}
}
3.在controller类下PersonController添加如下代码
@RequestMapping("/CommonResult")
public CommonResult<Object> getUserList() {
List<Person> personList2 = new ArrayList<>();
Person user1 = new Person("李四",18,123);
Person user2 = new Person("王五",15,321);
personList2.add(user1);
personList2.add(user2);
return new CommonResult<Object>(personList2);
}
好的,我们的统一返回格式已经大功告成,接下来测试。
3.测试接口
访问http://localhost:8080/CommonResult
返回结果如下
{"status":1,
"errorCode":"",
"errorMsg":"",
"resultBody":[{"name":"李四","age":18,"phoneNumber":123},{"name":"王五","age":15,"phoneNumber":321}]}
下面我们随便访问一个不存在的接口,如http://localhost:8080/aaa
,(这里用浏览器可能直接Whitelabel Error Page)用接口测试工具可以返回如下结果
{"timestamp":1603540190184,
"status":404,"error":
"Not Found",
"message":"","path":"/aaa"}
总结
本文仅是对Spring Boot的json处理及统一返回做一个实例,是本人在学习过程中的总结与实践。