一、返回 JSON 数据

1.1 JSON 数据实例

JSON 数据是 JavaScript 中的一种数据格式,并不是一种新语言,使用这种格式的数据传输,下面列举一些 JSON 格式数据的使用场景:

  • 在 ajax 中运用的非常广泛
  • 在 前后端分离的项目数据的传输就以 JSON 传输
  • restful 格式的 API 就是使用的 JSON 数据

下面给大家展示 一下 JSON 格式数据实例

  1. [
  2. {
  3. "id":1,
  4. "name":"xxx",
  5. },
  6. {
  7. "id":2,
  8. "name":"xxx",
  9. }, {
  10. "id":3,
  11. "name":"xxx",
  12. },
  13. ]
  14. {
  15. "code":200,
  16. "meta": {
  17. "msg": "xxxx",
  18. "result": [1,3,2,4]
  19. }
  20. }

1.2 SpringBoot 返回数据

我们编写一个 Controller 返回一个字符串数据,这里就和 SpringMVC 里的返回字符串是一样的,这里我们使用 RestController 注解返回 JSON 格式的数据,这个注解是 SpringBoot 2.x 中新增的一个注解

1.2.1 返回普通字符串

使用 RestController 指定可以返回 JSON 字符串也可以返回 普通字符串,我查看这个注解的源码
image.png
得出结论:RestController = Controller + RespouseBody

  1. package cn.gorit.controller;
  2. import org.springframework.web.bind.annotation.RequestMapping;
  3. import org.springframework.web.bind.annotation.RestController;
  4. @RestController
  5. public class TestController {
  6. @RequestMapping("/")
  7. public String Hello() {
  8. return "Hello World";
  9. }
  10. }

image.png

1.2.2 返回 JSON 对象

创建一个实体类 (User)

  1. public class User {
  2. private Integer id;
  3. private String username;
  4. private String password;
  5. // getter 和 setter,有参数(无参)构造,toString方法 省略
  6. }

在 Controller 中添加一个返回实体类对象的方法

  1. // 返回对象
  2. @RequestMapping("/user")
  3. public User getUser() {
  4. return new User(1,"coco","ddd");
  5. }

访问:http://localhost:8080/user 可以看到如下 JSON 数据

  1. {"id":1,"username":"coco","password":"ddd"}

1.2.3 返回 JSON 集合

编写 Controller 返回 List 集合

  1. // 返回集合
  2. @RequestMapping("/list")
  3. public List<User> getUserList() {
  4. List<User> list = new ArrayList<User>();
  5. list.add(new User(1,"coco","coco"));
  6. list.add(new User(2,"aaaa","aaa"));
  7. list.add(new User(3,"vvvv","vvvvv"));
  8. return list;
  9. }

访问:http://localhost:8080/list 可以看到如下 JSON 集合

  1. [{"id":1,"username":"coco","password":"coco"},{"id":2,"username":"aaaa","password":"aaa"},{"id":3,"username":"vvvv","password":"vvvvv"}]

1.2.4 返回 map 集合

编写 controller 返回 map 集合

  1. {"作者信息":{"id":1,"username":"Gorit","password":""},"作者个人网站":"https://www.gorit.cn","粉丝数量":289}

1.3 添加 jackson 工具类,处理空值对象问题

1.3.1 使用 默认的 jackson处理控制

我们向前端传递一个 map 对象,如果遇到空值的话,就自动替换为字符串,就像上面的效果

  1. package cn.gorit.config;
  2. import com.fasterxml.jackson.core.JsonGenerator;
  3. import com.fasterxml.jackson.databind.JsonSerializer;
  4. import com.fasterxml.jackson.databind.ObjectMapper;
  5. import com.fasterxml.jackson.databind.SerializerProvider;
  6. import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
  7. import org.springframework.context.annotation.Bean;
  8. import org.springframework.context.annotation.Configuration;
  9. import org.springframework.context.annotation.Primary;
  10. import org.springframework.http.converter.json.Jackson2ObjectMapperBuilder;
  11. import java.io.IOException;
  12. // 编写配置类,将返回诶 null 的数据转换为 ""
  13. @Configuration
  14. public class JacksonConfig {
  15. @Bean
  16. @Primary
  17. @ConditionalOnMissingBean(ObjectMapper.class)
  18. public ObjectMapper jacksonObjectMapper(Jackson2ObjectMapperBuilder builder) {
  19. ObjectMapper objectMapper = builder.createXmlMapper(false).build();
  20. objectMapper.getSerializerProvider().setNullValueSerializer(new JsonSerializer<Object>() {
  21. @Override
  22. public void serialize(Object o, JsonGenerator jsonGenerator, SerializerProvider serializerProvider) throws IOException, IOException {
  23. jsonGenerator.writeString("");
  24. }
  25. });
  26. return objectMapper;
  27. }
  28. }

1.3.2 使用 fastjson 处理空值

使用 fastjson 需要导入如下依赖

  1. <dependency>
  2. <groupId>com.alibaba</groupId>
  3. <artifactId>fastjson</artifactId>
  4. <version>1.2.35</version>
  5. </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 数据返回总结

本章节的主要学习内容

  1. 使用 SpringBoot 返回 JSON 数据
  2. jackson 和 fastjson 对 JSON 数据 空值的处理
  3. 规格化 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 ,就可以看到效果了

image.png

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

然后访问根路径 /,可以看到过滤器被激活了
image.png

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 销毁");
    }
}

image.png