【笑小枫的SpringBoot系列】【四】SpringBoot返回统一结果包装

简介: 【笑小枫的SpringBoot系列】【四】SpringBoot返回统一结果包装

为什么?


前后端分离的时代,如果没有统一的返回格式,给前端的结果各式各样,估计前端的小伙伴就要骂娘了。

我们想对自定义异常抛出指定的状态码排查错误,对系统的不可预知的异常抛出友好一点的异常信息。

我们想让接口统一返回一些额外的数据,例如接口执行的时间等等。

所以嘛,不管是日常和前端小伙伴对接,还是和其他部门进行接口对接,都应该返回固定的格式。

本文给出一个简单通用的返回格式,小伙伴们有需求,可以基于此版本根据自己的业务需求丰富返回格式。


返回数据格式如下👇,有兴趣的小伙伴们可以继续往下看SpringBoot是怎么来实现的。

{
  "status": true,
  "code": "0000",
  "msg": "",
  "data": {
    "id": 1,
    "deptId": 103,
    "userName": "admin",
    "nickName": "笑小枫",
    "userType": "00",
    "email": "xxf@163.com",
    "phone": "15888888888",
    "status": "0",
    "remark": "管理员"
  }
}


怎么做?🧐


首先创建一个测试的Controller,代码如下👇


package com.maple.demo.controller;
import com.maple.demo.util.ResultJson;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author 笑小枫
 * @date 2022/07/15
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/example")
public class TestResultController {
    @GetMapping("/testResult")
    public Test testResult() {
        Test test = new Test();
        test.setName("笑小枫");
        test.setAge(18);
        test.setRemark("大家好,我是笑小枫,喜欢我的小伙伴点个赞呗");
        return test;
    }
    @GetMapping("/testResultJson")
    public ResultJson testResultJson() {
        Test test = new Test();
        test.setName("笑小枫");
        test.setAge(18);
        test.setRemark("大家好,我是笑小枫,喜欢我的小伙伴点个赞呗");
        return new ResultJson(test);
    }
    @Data
    static class Test {
        private String name;
        private Integer age;
        private String remark;
    }
}

我们先看看这是调用返回的结果👀

{
  "name": "笑小枫",
  "age": 18,
  "remark": "大家好,我是笑小枫,喜欢我的小伙伴点个赞"
}

然后在util包下定义一个统一的返回格式类,代码如下👇

package com.maple.demo.util;
import lombok.Data;
/**
 * 统一返回信息包装类
 *
 * @author 笑小枫
 * @date 2022/07/15
 */
@Data
public class ResultJson {
    private static final String SUCCESS_CODE = "0000";
    /**
     * 成功失败的状态值,true:成功;false:失败
     * 这里返回编码为:“0000”,系统就认为接口成功;其他值,代表失败
     */
    private Boolean status;
    /**
     * 状态码 正确为0000
     */
    private String code;
    /**
     * 返回提示信息
     */
    private String msg;
    /**
     * 返回数据
     */
    private Object data;
    public ResultJson() {
        this.status = true;
        this.code = SUCCESS_CODE;
        this.msg = "";
    }
    public ResultJson(Object data) {
        this.status = true;
        this.code = SUCCESS_CODE;
        this.msg = "";
        this.data = data;
    }
    public ResultJson(String code, String msg) {
        this.status = SUCCESS_CODE.equals(code);
        this.code = code;
        this.msg = msg;
    }
    /**
     * 如果返回状态码非0000,且接口状态为成功,请使用这个
     * @param status 接口请求状态
     * @param code 返回code值
     * @param msg 返回消息
     * @param data 返回数据
     */
    public ResultJson(Boolean status, String code, String msg, Object data) {
        this.status = status;
        this.code = code;
        this.msg = msg;
        this.data = data;
    }
}


普通版,代码侵入性强


这样我们可以把结果统一放在ResultJson里面返回,代码如下👇

package com.maple.demo.controller;
import com.maple.demo.util.ResultJson;
import lombok.Data;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
/**
 * @author 笑小枫
 * @date 2022/07/15
 */
@RestController
@RequiredArgsConstructor
@RequestMapping("/example")
public class TestResultController {
    @GetMapping("/testResult")
    public Test testResult() {
        Test test = new Test();
        test.setName("笑小枫");
        test.setAge(18);
        test.setRemark("大家好,我是笑小枫,喜欢我的小伙伴点个赞呗");
        return test;
    }
    /**
     * 普通的返回
     */
    @GetMapping("/testResultJson")
    public ResultJson testResultJson() {
        Test test = new Test();
        test.setName("笑小枫");
        test.setAge(18);
        test.setRemark("大家好,我是笑小枫,喜欢我的小伙伴点个赞呗");
        return new ResultJson(test);
    }
    @Data
    static class Test {
        private String name;
        private Integer age;
        private String remark;
    }
}

我们看一下返回的结果👇

{
  "status": true,
  "code": "0000",
  "msg": "",
  "data": {
    "name": "笑小枫",
    "age": 18,
    "remark": "大家好,我是笑小枫,喜欢我的小伙伴点个赞呗"
  }
}


切面处理,代码无侵入


上述代码虽然实现了功能,但所有的返回结果都要处理,对代码有比较强的侵入,Spring拥有各种切面的支持,让我们看看如何代码无侵入的实现这个功能。

最后创建一个配置类,添加@RestControllerAdvice注解,代码如下👇

package com.maple.demo.config;
import com.alibaba.fastjson.JSON;
import com.maple.demo.util.ResultJson;
import lombok.extern.slf4j.Slf4j;
import org.jetbrains.annotations.NotNull;
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.mvc.method.annotation.ResponseBodyAdvice;
/**
 * 对返回结果统一进行处理,包括返回结果格式统一包装,返回异常统一处理
 *
 * @author 笑小枫
 * @date 2022/07/15
 */
@Slf4j
@RestControllerAdvice
public class RestResponseAdvice implements ResponseBodyAdvice<Object> {
    @Override
    public boolean supports(@NotNull MethodParameter returnType,
                            @NotNull Class<? extends HttpMessageConverter<?>> converterType) {
        return true;
    }
    /**
     * 返回结果包装统一返回格式
     *
     * @return 包装后的返回结果
     */
    @Override
    public Object beforeBodyWrite(Object body,
                                  @NotNull MethodParameter returnType,
                                  @NotNull MediaType selectedContentType,
                                  @NotNull Class<? extends HttpMessageConverter<?>> selectedConverterType,
                                  @NotNull ServerHttpRequest request,
                                  ServerHttpResponse response) {
        // 指定返回的结果为application/json格式
        // 不指定,String类型转json后返回Content-Type是text/plain;charset=UTF-8
        response.getHeaders().setContentType(MediaType.APPLICATION_JSON);
        ResultJson result = new ResultJson(body);
        // 若返回类型是ResultJson,则不进行修改
        if (body == null) {
            if (returnType.getParameterType().isAssignableFrom(String.class)) {
                return JSON.toJSONString(result);
            }
        } else if (body instanceof ResultJson) {
            return body;
        } else if (body instanceof String) {
            return JSON.toJSONString(result);
        }
        return result;
    }
}


这样就完成了统一结果的返回✌

我们再请求文章最开始写的测试方法http://127.0.0.1:6666/testResult看一下返回的结果👀

{
  "status": true,
  "code": "0000",
  "msg": "",
  "data": {
    "name": "笑小枫",
    "age": 18,
    "remark": "大家好,我是笑小枫,喜欢我的小伙伴点个赞呗"
  }
}


这样统一返回格式就完成了,对代码没有的侵入,原来的代码该怎么写还是怎么写,是不是很nice。


@RestControllerAdvice介绍😴


首先了解一下SpringBoot的@RestControllerAdvice注解,它是Spring框架3.2新增的的注解

点进去这个注解源码,可以看到它是由@ControllerAdvice和@ResponseBody的组合注解

它通常用来定义@ExceptionHandler, @InitBinder以及@ModelAttribute适用于所有方法@RequestMapping的方法。

直白点说,这就是一个增强Controller的注解。主要实现以下三个方面的功能


  1. 全局异常处理
  2. 全局数据预处理
  3. 全局数据绑定

我们看一下@RestControllerAdvice注解的源码

@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
@Documented
@ControllerAdvice
@ResponseBody
public @interface RestControllerAdvice {
    @AliasFor(
            annotation = ControllerAdvice.class
    )
    String[] value() default {};
    @AliasFor(
            annotation = ControllerAdvice.class
    )
    String[] basePackages() default {};
    @AliasFor(
            annotation = ControllerAdvice.class
    )
    Class<?>[] basePackageClasses() default {};
    @AliasFor(
            annotation = ControllerAdvice.class
    )
    Class<?>[] assignableTypes() default {};
    @AliasFor(
            annotation = ControllerAdvice.class
    )
    Class<? extends Annotation>[] annotations() default {};
}


使用@RestControllerAdvice常做的两个功能的实现

  1. 返回统一格式的结果
  2. 异常统一处理


小结


好啦,本文就到这里了,我们简单的总结一下,主要介绍了以下内容👇👇

  • 为什么要返回统一的结果信息
  • 手动封装统一的返回信息
  • 使用@RestControllerAdvice自动封装返回信息
  • 简单介绍@RestControllerAdvice注解


关于笑小枫💕


本章到这里结束了,喜欢的朋友关注一下我呦😘😘,大伙的支持,就是我坚持写下去的动力。

老规矩,懂了就点赞收藏;不懂就问,日常在线,我会就会回复哈~🤪

后续文章会陆续更新,文档会同步在微信公众号、个人博客、CSDN和GitHub保持同步更新。😬

微信公众号:笑小枫

笑小枫个人博客:https://www.xiaoxiaofeng.com

CSDN:https://zhangfz.blog.csdn.net

本文源码:https://github.com/hack-feng/maple-demo


目录
相关文章
|
数据采集 前端开发 Java
SpringBoot使用@RestControllerAdvice无侵入返回统一结果包装
SpringBoot使用@RestControllerAdvice无侵入返回统一结果包装
528 0
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 实现动态路由和菜单功能,快速搭建前后端分离的应用框架。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,包括版本兼容性、安全性、性能调优等方面。
148 1
|
2月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的服装商城管理系统
基于Java+Springboot+Vue开发的服装商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的服装商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
153 2
基于Java+Springboot+Vue开发的服装商城管理系统
|
2月前
|
前端开发 JavaScript Java
SpringBoot项目部署打包好的React、Vue项目刷新报错404
本文讨论了在SpringBoot项目中部署React或Vue打包好的前端项目时,刷新页面导致404错误的问题,并提供了两种解决方案:一是在SpringBoot启动类中配置错误页面重定向到index.html,二是将前端路由改为hash模式以避免刷新问题。
232 1
|
20天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,创建并配置 Spring Boot 项目,实现后端 API;然后,使用 Ant Design Pro Vue 创建前端项目,配置动态路由和菜单。通过具体案例,展示了如何快速搭建高效、易维护的项目框架。
95 62
|
18天前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能
本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个前后端分离的应用框架,实现动态路由和菜单功能。首先,确保开发环境已安装必要的工具,然后创建并配置 Spring Boot 项目,包括添加依赖和配置 Spring Security。接着,创建后端 API 和前端项目,配置动态路由和菜单。最后,运行项目并分享实践心得,帮助开发者提高开发效率和应用的可维护性。
36 2
|
21天前
|
JavaScript Java 项目管理
Java毕设学习 基于SpringBoot + Vue 的医院管理系统 持续给大家寻找Java毕设学习项目(附源码)
基于SpringBoot + Vue的医院管理系统,涵盖医院、患者、挂号、药物、检查、病床、排班管理和数据分析等功能。开发工具为IDEA和HBuilder X,环境需配置jdk8、Node.js14、MySQL8。文末提供源码下载链接。
|
2月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
基于Java+Springboot+Vue开发的大学竞赛报名管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的大学竞赛报名管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
219 3
基于Java+Springboot+Vue开发的大学竞赛报名管理系统
|
1月前
|
JavaScript 安全 Java
如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用
【10月更文挑战第8天】本文介绍了如何使用 Spring Boot 和 Ant Design Pro Vue 构建一个具有动态路由和菜单功能的前后端分离应用。首先,通过 Spring Initializr 创建并配置 Spring Boot 项目,实现后端 API 和安全配置。接着,使用 Ant Design Pro Vue 脚手架创建前端项目,配置动态路由和菜单,并创建相应的页面组件。最后,通过具体实践心得,分享了版本兼容性、安全性、性能调优等注意事项,帮助读者快速搭建高效且易维护的应用框架。
41 3
|
2月前
|
前端开发 JavaScript Java
基于Java+Springboot+Vue开发的蛋糕商城管理系统
基于Java+Springboot+Vue开发的蛋糕商城管理系统(前后端分离),这是一项为大学生课程设计作业而开发的项目。该系统旨在帮助大学生学习并掌握Java编程技能,同时锻炼他们的项目设计与开发能力。通过学习基于Java的蛋糕商城管理系统项目,大学生可以在实践中学习和提升自己的能力,为以后的职业发展打下坚实基础。
157 3
基于Java+Springboot+Vue开发的蛋糕商城管理系统