一、返回 JSON 数据
1.1 JSON 数据实例
JSON 数据是 JavaScript 中的一种数据格式,并不是一种新语言,使用这种格式的数据传输,下面列举一些 JSON 格式数据的使用场景:
- 在 ajax 中运用的非常广泛
- 在 前后端分离的项目数据的传输就以 JSON 传输
- restful 格式的 API 就是使用的 JSON 数据
下面给大家展示 一下 JSON 格式数据实例
[
{
"id":1,
"name":"xxx",
},
{
"id":2,
"name":"xxx",
}, {
"id":3,
"name":"xxx",
},
]
{
"code":200,
"meta": {
"msg": "xxxx",
"result": [1,3,2,4]
}
}
1.2 SpringBoot 返回数据
我们编写一个 Controller 返回一个字符串数据,这里就和 SpringMVC 里的返回字符串是一样的,这里我们使用 RestController 注解返回 JSON 格式的数据,这个注解是 SpringBoot 2.x 中新增的一个注解
1.2.1 返回普通字符串
使用 RestController 指定可以返回 JSON 字符串也可以返回 普通字符串,我查看这个注解的源码
得出结论:RestController = Controller + RespouseBody
package cn.gorit.controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
@RestController
public class TestController {
@RequestMapping("/")
public String Hello() {
return "Hello World";
}
}
1.2.2 返回 JSON 对象
创建一个实体类 (User)
public class User {
private Integer id;
private String username;
private String password;
// getter 和 setter,有参数(无参)构造,toString方法 省略
}
在 Controller 中添加一个返回实体类对象的方法
// 返回对象
@RequestMapping("/user")
public User getUser() {
return new User(1,"coco","ddd");
}
访问:http://localhost:8080/user 可以看到如下 JSON 数据
{"id":1,"username":"coco","password":"ddd"}
1.2.3 返回 JSON 集合
编写 Controller 返回 List 集合
// 返回集合
@RequestMapping("/list")
public List<User> getUserList() {
List<User> list = new ArrayList<User>();
list.add(new User(1,"coco","coco"));
list.add(new User(2,"aaaa","aaa"));
list.add(new User(3,"vvvv","vvvvv"));
return list;
}
访问:http://localhost:8080/list 可以看到如下 JSON 集合
[{"id":1,"username":"coco","password":"coco"},{"id":2,"username":"aaaa","password":"aaa"},{"id":3,"username":"vvvv","password":"vvvvv"}]
1.2.4 返回 map 集合
编写 controller 返回 map 集合
{"作者信息":{"id":1,"username":"Gorit","password":""},"作者个人网站":"https://www.gorit.cn","粉丝数量":289}
1.3 添加 jackson 工具类,处理空值对象问题
1.3.1 使用 默认的 jackson处理控制
我们向前端传递一个 map 对象,如果遇到空值的话,就自动替换为字符串,就像上面的效果
package cn.gorit.config;
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;
// 编写配置类,将返回诶 null 的数据转换为 ""
@Configuration
public class JacksonConfig {
@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, IOException {
jsonGenerator.writeString("");
}
});
return objectMapper;
}
}
1.3.2 使用 fastjson 处理空值
使用 fastjson 需要导入如下依赖
<dependency>
<groupId>com.alibaba</groupId>
<artifactId>fastjson</artifactId>
<version>1.2.35</version>
</dependency>
处理 null 值配置类
使用 fastjson 的时候需要继承 WebMvcConfigurationSupport 类, 覆盖 configureMessageConverters 方法,在方法中在指定要转换 null 的场景
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.context.annotation.Configuration;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurationSupport;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.List;
@Configuration public class fastJsonConfig extends WebMvcConfigurationSupport {
/**
* 使用阿里 FastJson 作为JSON MessageConverter
* @param converters
*/
@Override
public void configureMessageConverters( List<HttpMessageConverter<?> > converters )
{
FastJsonHttpMessageConverter converter = new FastJsonHttpMessageConverter();
FastJsonConfig config = new FastJsonConfig();
config.setSerializerFeatures (
/* 保留map空的字段 */
SerializerFeature.WriteMapNullValue,
/* 将String类型的null转成""
SerializerFeature.WriteNullStringAsEmpty,
// 将Number类型的null转成0
SerializerFeature.WriteNullNumberAsZero,
// 将List类型的null转成[]
SerializerFeature.WriteNullListAsEmpty,
// 将Boolean类型的null转成false
SerializerFeature.WriteNullBooleanAsFalse,
// 避免循环引用
SerializerFeature.DisableCircularReferenceDetect); */
converter.setFastJsonConfig( config );
converter.setDefaultCharset( Charset.forName( "UTF-8" ) );
List<MediaType> mediaTypeList = new ArrayList<>();
/* 解决中文乱码问题,相当于在Controller上的@RequestMapping中加了个属 性produces = "application/json" */
mediaTypeList.add(MediaType.APPLICATION_JSON);
converter.setSupportedMediaTypes(mediaTypeList);
converters.add(converter);
}
}
1.4 封装统一返回的数据结构
在上面,我们可以看到基本返回的 JSON 数据结构,但是这和我们看到一些网站上的不太一样,为什么人家的网站返回的 JSON 数据还有状态码,状态信息呢?因此我们可以定义如下的数据结构
1.4.1 自定义统一的返回 json 结构
由于封装的 json 数据类型不能确定,所以在定义统一的 json结构时,我们需要用到泛型,因此统一的 json 结构就包含数据,状态码,提示信息即可,构造方法根据真实的业务场景实时更改即可,
编写 JsonResult 实体类
package cn.gorit.pojo.result;
// 定义统一的 JSON 结构
/***
* json 数据返回的不确定,在定义 json 结构时,需要用泛型
* 统一的 json 结构中属性包含数据、状态码、提示信息
*/
public class JsonResult<T> {
private T data; // 数据
private String code; // 返回状态码
private String msg;
/**
* 没有数据返回,默认状态码为 0
* */
public JsonResult() {
this.code = "0";
this.msg = "操作成功";
}
/**
* 没有数据返回时,人为指定状态码和提示信息
* */
public JsonResult(String code, String msg) {
this.code = code;
this.msg = msg;
}
/**
* 有数据返回,状态码为0,默认提示信息为,操作成功
* */
public JsonResult(T data) {
this.code = "0";
this.msg = "操作成功";
this.data = data;
}
/**
* 有数据返回,状态码为0,人为指定提示信息
* */
public JsonResult(T data, String msg) {
this.code = "0";
this.msg = msg;
this.data = data;
}
// getter 和 setter 省略
}
1.4.2 编写返回 jsonresult 的 controller
package cn.gorit.controller;
import cn.gorit.pojo.entity.User;
import cn.gorit.pojo.result.JsonResult;
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
@RequestMapping("/jsonresult")
public class JsonController {
@RequestMapping("/user")
public JsonResult<User> getUser() {
User user = new User(1,"Gorit","224433");
return new JsonResult<>(user);
}
@RequestMapping("/list")
public JsonResult<List> getUserList() {
List<User> list = new ArrayList<>();
list.add(new User(1,"coco","22435"));
list.add(new User(2,"cici","123131"));
list.add(new User(3,"Gorit","213dewqd3qw3212eq3d3de32"));
return new JsonResult<>(list,"获取用户列表成功");
}
@RequestMapping("/map")
public JsonResult<Map> getMap() {
Map<String,Object> map = new HashMap<>(3);
User user = new User(1,"Gorit","224433");
map.put("作者",user);
map.put("博客地址","http://www.gorit.cn");
map.put("csdn",null);
map.put("粉丝数量",4153);
return new JsonResult<>(map);
}
}
访问 http://localhost:8080/jsonresult/user
{"data":{"id":1,"username":"Gorit","password":"224433"},"code":"0","msg":"操作成功"}
访问 http://localhost:8080/jsonresult/list
{"data":[{"id":1,"username":"coco","password":"22435"},{"id":2,"username":"cici","password":"123131"},{"id":3,"username":"Gorit","password":"213dewqd3qw3212eq3d3de32"}],"code":"0","msg":"获取用户列表成功"}
访问 http://localhost:8080/jsonresult/map
{"data":{"作者":{"id":1,"username":"Gorit","password":"224433"},"csdn":"","粉丝数量":4153,"博客地址":"http://www.gorit.cn"},"code":"0","msg":"操作成功"}
1.5 JSON 数据返回总结
本章节的主要学习内容
- 使用 SpringBoot 返回 JSON 数据
- jackson 和 fastjson 对 JSON 数据 空值的处理
- 规格化 JSON 数据
二、设置静态资源访问
2.1 yml 配置
新建的 SpringBoot 项目中,resources 目录下有一个 static 文件,我们在里面存放一张图片,然后再 application.yml 中加入访问的配置
spring:
mvc:
static-path-pattern: "/static/**"
resources:
static-locations: "classpath:/static/"
注意:
如果是 yml 中写配置的话,有路径的要加引号,不然无法识别,这个 bug 卡了我好久的
然后启动 SpringBoot 项目,访问 http://localhost:8080/static/pay.jpg ,就可以看到效果了
2.2 Java 编码配置
我们需要实现 WebMvcConfigurer 接口即可,然后实现 addResourceHandlers 方法,添加路径匹配规则即可
package cn.gorit.config;
import org.springframework.context.annotation.Configuration;
import org.springframework.web.servlet.config.annotation.ResourceHandlerRegistry;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
@Configuration
public class WebMvcConfig implements WebMvcConfigurer {
@Override
public void addResourceHandlers(ResourceHandlerRegistry registry) {
registry
.addResourceHandler("/static/**")
.addResourceLocations("classpath:/static/");
}
}
然后就能看到效果了
三、文件上传
3.1 单文件上传
在 static 目录下 创建一个 upload.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>文件上传</title>
</head>
<body>
<form action="/upload" method="post" enctype="multipart/form-data">
<input type="file" name="uploadFile" value="请选择文件">
<input type="submit" value="上传">
</form>
</body>
</html>
3.2 编写 文件上传处理的 Controller
package cn.gorit.controller;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;
import javax.servlet.http.HttpServletRequest;
import java.io.File;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.UUID;
// 编写文件上传的接口
@RestController
public class FileUploadController {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
@PostMapping("/upload")
public String upload(MultipartFile uploadFile, HttpServletRequest request) {
// 表示上传文件的保存路径为项目运行目录下的 uploadFile 文件夹
String realPath =
request.
getSession().getServletContext().getRealPath("/uploadFile/"); // 这里对应 input file 的 name
String format = sdf.format(new Date());
File folder = new File(realPath + format);
// 如果上传的目录不存在就创建一个
if (!folder.isDirectory()) {
folder.mkdirs();
}
String oldName = uploadFile.getOriginalFilename();
// 给上传的文件进行重新命名
String newName = UUID.randomUUID().toString() + oldName.substring(oldName.lastIndexOf("."),oldName.length());
try {
// 保存文件
uploadFile.transferTo(new File(folder, newName));
// 生成上传文件访问路径
String filePath = request.getScheme() + "://" + request.getServerName() + ":" +request.getServerPort() + "/uploadFile/" + format + newName;
return filePath;
} catch (IOException e) {
e.printStackTrace();
}
return "上传失败";
}
}
四、使用 过滤器 和 监听器
4.1 SpringBoot 使用过滤器 Filter
Filter 称为过滤器,它是 Servlet 中的三大核心组件之一,开发人员可以通过 Filter 技术管理服务器的所有资源,例如对 JSP,css,js 等静态文件的拦截,从而实现一些特殊的功能。也可以控制页面编码,URL 级别的权限控制,过滤敏感词汇等等
由于篇幅原因,这里不再介绍 Filter 的特性,我们直接上手 SpringBoot 整合 Filter。需要回顾知识的,可以回看 JavaEE 部分第二节,过滤器 与 监听器
创建一个 SpringBoot 应用,然后创建一个类,实现 Filter 接口(是 javax.servlet 包下的)
package cn.gorit.filter;
import javax.servlet.*;
import javax.servlet.annotation.WebFilter;
import java.io.IOException;
/**
* @Classname MyFilter
* @Description TODO
* @Date 2020/11/28 12:51
* @Created by CodingGorit
* @Version 1.0
*/
/**
* 使用 @WebFilter 用于将一个类声明为一个过滤器, 容器会根据具体的属性配置将相应的类部署为 过滤器。这样在 Web 应用中,我们不需要在 web.xml 中进行任何配置
*
其中 fileName 用于指定过滤器的name
urlPatterns 用于指定过滤规则
*/
@WebFilter(filterName = "myFilter",urlPatterns = "/")
public class MyFilter implements Filter {
@Override
public void init(FilterConfig filterConfig) throws ServletException {
System.out.println("MyFilter init ~");
}
@Override
public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
System.out.println("doFilter ~");
// filter 会经过过滤器链,这代表放行,如果不加这句话,就不会执行
filterChain.doFilter(servletRequest, servletResponse);
}
@Override
public void destroy() {
System.out.println("MyFilter destory ~");
}
}
但是,这样就完了吗?我们使用的是 Spring 所管理的项目,而 Servlet,Filter,Listener 是属于 JavaEE 的范畴,所以我们需要在主配置类下添加 @ServletComponentScan 注解,这样就可以直接通过 @WebServlet,@WebFilter,@WebListener
4.2 SpringBoot 使用监听器 Listener
package cn.gorit.listener;
import javax.servlet.ServletContextEvent;
import javax.servlet.ServletContextListener;
import javax.servlet.annotation.WebListener;
/**
* @Classname MyListener
* @Description TODO
* @Date 2020/11/28 13:25
* @Created by CodingGorit
* @Version 1.0
*/
// @WebListener 显示的将一个类声明为一个监听器
@WebListener
public class MyListener implements ServletContextListener {
// Servlet 容器启动 Web 应用调用该方法,在调用完该方法后,容器再对 Filter 初始化,然后再对 需要初始化的 Servlet 进行初始化
@Override
public void contextInitialized(ServletContextEvent sce) {
System.out.println("监听器 ServletContext 初始化");
}
// Servlet 容器终止 Web应用时,所调用的方法,再调用该方法前,容器会先销毁 Filter 和 Servlet 过滤器
@Override
public void contextDestroyed(ServletContextEvent sce) {
System.out.println("监听器 ServletContext 销毁");
}
}