Spring Boot进阶(45): Spring Boot 如何返回统一结果包装?一文教会你 | 超级详细,建议收藏

news2024/11/23 16:10:14

1. 前言🔥

        现如今是前后端分离的时代,如果没有统一的返回格式,给前端的结果各式各样,估计前端小伙伴就要骂街了。我们想对自定义异常抛出指定的状态码排查错误,对系统的不可预知的异常抛出友好一点的异常信息。我们想让接口统一返回一些额外的数据,例如接口执行的时间等等。

        所以嘛,不管是日常和前端小伙伴对接,还是和其他部门进行接口对接,都应该返回固定的格式。本文给出一个简单通用的返回格式,小伙伴们有需求,可以基于此版本根据自己的业务需求丰富返回格式。

        统一结果的返回数据格式如下 ,有兴趣的小伙伴们可以继续往下看SpringBoot是怎么来实现的。演示如下:

{
  "code": 200,
  "msg": "成功",
  "serverTime": 1686726816292,
  "data": [
    {
      "id": 1,
      "name": "刘亦菲",
      "age": 70,
      "sex": "女",
      "address": "上海闵行",
      "describes": "这是db1的用户",
      "image": "bug菌.jpg"
    }
  ],
  "success": true
}

        那么,具体如何封装包装呢?这将又会是干货满满的一期,全程无尿点不废话只抓重点教,具有非常好的学习效果,拿好小板凳准备就坐!希望学习的过程中大家认真听好好学,学习的途中有任何不清楚或疑问的地方皆可评论区留言或私信,bug菌将第一时间给予解惑,那么废话不多说,直接开整!Fighting!! 

2. 环境说明🔥

本地的开发环境:

  • 开发工具:IDEA 2021.3
  • JDK版本: JDK 1.8
  • Spring Boot版本:2.3.1 RELEASE
  • Maven版本:3.8.2

3. 正文🔥 

        看了如下格式演示,想必有的小伙伴就很好奇,这到底是如何实现的?很简单,接下来我就带着大家手把手教学。

3.1 定义统一返回格式类

        首先我们先定义好一个统一的返回格式类,代码如下:

/**
 * 接口返回结果,不带对象的
 */
@ApiModel(value = "基础返回信息", description = "基础返回信息")
public class BaseResponse implements Serializable {

    private static final long serialVersionUID = -7671756385477179547L;

    /**
     * api返回码
     */
    @ApiModelProperty("返回编码")
    protected Integer code = ResultEnum.SUCCESS.getKey();
    /**
     * api返回消息
     */
    @ApiModelProperty("返回信息")
    protected String msg = ResultEnum.SUCCESS.getValue();

    /**
     * 服务器时间
     */
    @ApiModelProperty("系统时间")
    protected long serverTime = System.currentTimeMillis();

    public BaseResponse() {

    }

    public BaseResponse(IEnum iEnum) {
        this.code = iEnum.getKey();
        this.msg = iEnum.getValue();
    }

    public BaseResponse(String msg) {
        this.msg = msg;
    }

    public Integer getCode() {
        return code;
    }

    public void setCode(Integer code) {
        this.code = code;
    }

    public String getMsg() {
        return msg;
    }

    public void setMsg(String msg) {
        this.msg = msg;
    }

    public long getServerTime() {
        return serverTime;
    }

    public void setServerTime(long serverTime) {
        this.serverTime = serverTime;
    }

    /**
     * 是否成功
     */
    @ApiModelProperty("是否成功")
    public boolean isSuccess() {
        return ResultEnum.SUCCESS.getKey().equals(code);
    }
}

        如上我们是定义了一个不带对象返回的同一类,如果我们要返回具体的数据对象呢?那我们再创建一个带对象返回的类即可。代码如下:

/**
 * 接口返回结果,带对象
 */
@ApiModel(value = "带内容的返回信息",description = "带内容的返回信息")
public class ResultResponse<T> extends BaseResponse {

	@ApiModelProperty("返回内容")
	private T data;

	public T getData() {
		return data;
	}

	public void setData(T data) {
		this.data = data;
	}

	public ResultResponse() {

	}

	public ResultResponse(T data) {
		this.data = data;
	}

	public ResultResponse(IEnum iEnum) {
		super(iEnum);
	}
}

3.2 定义一个Controller

        接下来我们来演示下如何使用它。我们其实只需要将ResultResponse作为返回类,然后在该方法中我们通过new ResultResponse<>(result)即可。代码如下:

    /**
     * 不分页查询db1所有用户信息
     */
    @GetMapping("/get-users1")
    @ApiOperation(value = "不分页查询db1所有用户信息", notes = "不分页查询db1所有用户信息")
    public ResultResponse<List<UserEntity>> getUserList1() {
        return new ResultResponse<>(userService.getUsers1());
    }

3.3 Swagger测试 

        我们通过Swagger来请求一下,我们可以看到。

         这样我们就生成了统一的返回包装,是不是看起来就优雅多了。

我们再来测试一下,如果返回报错,又该是如何?

我们还是定义一个请求,我们直接通过抛出异常试试。

    @GetMapping("/get-users1")
    public ResultResponse<List<UserEntity>> getUserList1() {
        return new ResultResponse<>(ErrorCodeEnum.SYSTEM_ERROR);
    }

我们请求Swagger来查阅下结果。

         结果依旧如此,未曾有改变,证明无论是正常返回还是异常抛出,皆不会影响。

        但是,你们有没有发现,这样的代码入侵性强,所有的返回结果都要处理。那有没有办法能一劳永逸的,每次的请求都会自动将其包装类修饰后返回呢?答案肯定是有的,spring拥有各种切面的支持,让我们看看如何代码无侵入的实现这个功能。

3.4  切面处理,代码无侵入

        我们来创建一个配置类,添加@RestControllerAdvice注解,代码如下:

import com.alibaba.fastjson.JSON;
import com.example.demo.vo.ResultResponse;
import lombok.extern.slf4j.Slf4j;
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;

import javax.validation.constraints.NotNull;

@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);
        ResultResponse result = new ResultResponse(body);
        // 若返回类型是ResultJson,则不进行修改
        if (body == null) {
            if (returnType.getParameterType().isAssignableFrom(String.class)) {
                return JSON.toJSONString(result);
            }
        } else if (body instanceof ResultResponse) {
            return body;
        } else if (body instanceof String) {
            return JSON.toJSONString(result);
        }
        return result;
    }
}

接下来,我们来测试一下,直接定义一个不带ResultResponse类包装的请求。

3.5 定义一个不带返回格式的Controller

        Controller代码如下:

    /**
     * 根据id查询区分性别返回不同结构体内容
     */
    @GetMapping("/get-user-for-id")
    @ApiOperation(value = "根据id查询区分性别返回不同结构体内容", notes = "根据id查询区分性别返回不同结构体内容")
    public List<UserEntity> getUserInfo(@RequestParam Integer id) {
        return userMapper.getUserByIdFor(id);
    }

        如上明显我是直接返回了,不带 ResultResponse类包装,我们来期待一下,切面是否生效。

3.6 Swagger测试请求

        我们访问Swagger地址:http://localhost:8080/doc.html,找到我们定义的请求。

         很明显,切面完全是设置上了,被统一返回体封装。这样统一返回格式就完成了,对代码没有的侵入,原来的代码该怎么写还是怎么写,是不是很nice很棒呢。

3.7 @RestControllerAdvice

使用@RestControllerAdvice,常做的两个功能的实现有如下。

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

        如上场景功能我上期也做了定义GlobalExceptionHandler全局异常处理器,同样是使用了@RestControllerAdvice注解进行功能点实现,这里我就不详细介绍了,有想法的小伙伴可以前去观摩一下。

3.8 小结 

好啦,本文就到就要接近尾声啦,我们来简单的做个总结,主要介绍了以下内容:

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

4. 热文推荐🔥

滴~如下推荐【Spring Boot 进阶篇】的学习大纲,请小伙伴们注意查收。

Spring Boot进阶(01):Spring Boot 集成 Redis,实现缓存自由

Spring Boot进阶(02):使用Validation进行参数校验

Spring Boot进阶(03):如何使用MyBatis-Plus实现字段的自动填充

Spring Boot进阶(04):如何使用MyBatis-Plus快速实现自定义sql分页

Spring Boot进阶(05):Spring Boot 整合RabbitMq,实现消息队列服务

Spring Boot进阶(06):Windows10系统搭建 RabbitMq Server 服务端

Spring Boot进阶(07):集成EasyPoi,实现Excel/Word的导入导出

Spring Boot进阶(08):集成EasyPoi,实现Excel/Word携带图片导出

Spring Boot进阶(09):集成EasyPoi,实现Excel文件多sheet导入导出

Spring Boot进阶(10):集成EasyPoi,实现Excel模板导出成PDF文件

Spring Boot进阶(11):Spring Boot 如何实现纯文本转成.csv格式文件?

Spring Boot进阶(12):Spring Boot 如何获取Excel sheet页的数量?

Spring Boot进阶(13):Spring Boot 如何获取@ApiModelProperty(value = “序列号“, name = “uuid“)中的value值name值?

Spring Boot进阶(14):Spring Boot 如何手动连接库并获取指定表结构?一文教会你

Spring Boot进阶(15):根据数据库连接信息指定分页查询表结构信息

Spring Boot进阶(16):Spring Boot 如何通过Redis实现手机号验证码功能?

Spring Boot进阶(17):Spring Boot如何在swagger2中配置header请求头等参数信息

Spring Boot进阶(18):SpringBoot如何使用@Scheduled创建定时任务?

Spring Boot进阶(19):Spring Boot 整合ElasticSearch

Spring Boot进阶(20):配置Jetty容器

Spring Boot进阶(21):配置Undertow容器

Spring Boot进阶(22):Tomcat与Undertow容器性能对比分析

Spring Boot进阶(23):实现文件上传

Spring Boot进阶(24):如何快速实现多文件上传?

Spring Boot进阶(25):文件上传的单元测试怎么写?

Spring Boot进阶(26):Mybatis 中 resultType、resultMap详解及实战教学

Spring Boot进阶(27):Spring Boot 整合 kafka(环境搭建+演示)

Spring Boot进阶(28):Jar包Linux后台启动部署及滚动日志查看,日志输出至实体文件保存

Spring Boot进阶(29):如何正确使用@PathVariable,@RequestParam、@RequestBody等注解?不会我教你,结合Postman演示

Spring Boot进阶(30):@RestController和@Controller 注解使用区别,实战演示

...

5. 文末🔥

        如果想系统性的学习Spring Boot,小伙伴们直接订阅bug菌专门为大家创建的Spring Boot专栏《滚雪球学Spring Boot》从入门到精通,从无到有,从零到一!以知识点+实例+项目的学习模式由浅入深对Spring Boot框架进行学习&使用。

       我是bug菌,一名想走👣出大山改变命运的程序猿。接下来的路还很长,都等待着我们去突破、去挑战。来吧,小伙伴们,我们一起加油!未来皆可期,fighting!

关注公众号,获取最新BAT互联网公司面试题、4000G pdf电子书籍、简历模板等硬核资源

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

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

相关文章

Java基础重点概要(部分)

为工信部第六届全国计算机信息大赛准备 &#xff0c;主要复习以下内容。 Java基础及环境&#xff1a;JDK发展历史&#xff0c;不同版本的进阶内容。Java程序的编写、编译、调试。 Java程序设计基础&#xff1a;常量和变量的概念&#xff0c;声明方式和作用域。基本数据类型的定…

基于Java学校运动会管理系统设计实现(源码+lw+部署文档+讲解等)

博主介绍&#xff1a; ✌全网粉丝30W,csdn特邀作者、博客专家、CSDN新星计划导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战 ✌ &#x1f345; 文末获取源码联系 &#x1f345; &#x1f447;&#x1f3fb; 精…

C语言之指针详解(4)

目录 本章重点 1. 字符指针 2. 数组指针 3. 指针数组 4. 数组传参和指针传参 5. 函数指针 6. 函数指针数组 7. 指向函数指针数组的指针 8. 回调函数 9. 指针和数组面试题的解析 函数指针 数组指针—指向数组的指针 函数指针—指向函数的指针 函数指针 我们来看代码 #…

基于人工智能,现代数据基础架构的新兴架构

作者 Matt Bornstein、Jennifer Li和Martin Casado 摘要 现代机器学习基础设施2.0新架构&#xff1a; http://bit.ly/3AVBpV6 这个图概括了机器学习基础设施2.0的主要组成部分。它涵盖了从数据转换到模型集成的全过程。每个阶段的具体工具和技术也在括号中列出。 结构解读 …

stub实验和配置命令

拓扑 需求 将区域12设置为Stub区域&#xff0c;使区域12的路由设备不受外部链路影响(不接收4/5类LSA&#xff09;降低区域12&#xff08;末梢区域&#xff09;设备压力&#xff0c;还能让区域12的PC1与外部PC3通信 配置步骤 1&#xff09;配置接口信息 - 配置PC的IP地址 - 配置…

SpringBoot的基础配置 - yaml文件的格式以及数据读取

文章目录 SpringBoot基础配置配置文件格式yaml文件格式yaml数据读取 SpringBoot基础配置 配置文件格式 我们用修改服务器端口号来举例, 演示配置的格式 目前我们SpringBoot入门程序已经可以启动, 但是端口是使用的默认的8080 http://localhost:8080/books/1修改服务器的端口号…

CleanMyMacX4.13.4中文免费版mac电脑管家

CleanMyMac X这款软件集成清理、mac保护、速度优化维护、应用程序管理和文件管理5大功能&#xff0c;使用过程安全高效&#xff0c;用户不必担心误操作导致系统的崩溃。作为一款专业的mac电脑系统管家&#xff0c;CleanMymac X一直致力于更加智能、便捷地全方位维护我们的电脑&…

基于servlet+jsp+mysql人事工资管理系统(含实训报告)

基于servletjspmysql人事工资管理系统 一、系统介绍二、功能展示1.用户登陆2.查看个人信息3.查看个人工资、查看考勤4.查看自己所在部门5.人员信息管理6.考勤管理&#xff08;管理员&#xff09;7.工资管理&#xff08;管理员&#xff09;8.部门管理&#xff08;管理员&#xf…

批量生成,本地推理,人工智能声音克隆框架PaddleSpeech本地批量克隆实践(Python3.10)

云端炼丹固然是极好的&#xff0c;但不能否认的是&#xff0c;成本要比本地高得多&#xff0c;同时考虑到深度学习的训练相对于推理来说成本也更高&#xff0c;这主要是因为它需要大量的数据、计算资源和时间等资源&#xff0c;并且对超参数的调整也要求较高&#xff0c;更适合…

大数据处理架构Hadoop

大数据处理架构 Hadoop 概述Hadoop简介Hadoop的特性 Hadoop项目架构Hadoop的安装和使用Hadoop的安装方式Hadoop的安装配置&#xff08;单机/伪分布式&#xff09;SSH登录权限设置单机安装配置伪分布式安装配置 Hadoop集群的部署与使用Hadoop集群中有哪些节点类型集群规模要多大…

Python高级系列教程:HTTP协议与静态Web服务器开发

学习目标 1、了解HTTP协议的基本概念 2、掌握HTTP请求报文与响应报文 3、学会使用开发者工具查看HTTP协议的通信过程 4、搭建Python自带的静态Web服务器 5、掌握Python静态Web服务器开发 一、HTTP协议概述 1、网址URL 网址又称为URL&#xff0c;URL的英文全拼是(Uniform …

Vue中如何进行表格合并与拆分

Vue中如何进行表格合并与拆分 在Vue应用程序中&#xff0c;表格是一个非常常见的组件。有时候我们需要对表格进行合并或拆分来满足特定的需求。在本文中&#xff0c;我们将介绍如何在Vue中进行表格的合并和拆分。 如何进行表格合并&#xff1f; 表格合并是指将多行或多列的单…

如何使用ArcGIS制作SketchUp格式三维建筑

GIS数据也可以和传统的三维建模软件进行结合&#xff0c;在很长一段时间内&#xff0c;一直有客户问如何将水经微图中下载的建筑数据转换为SketchUp模型&#xff0c;这里给大家找到了一种解决方案&#xff0c;可以通过插件进行转换&#xff0c;希望能够对你有所帮助。 加载插件…

恶劣天气条件下激光雷达感知研究综述

摘要 自动驾驶汽车依靠各种传感器来收集周围环境的信息。车辆的行为是根据环境感知进行规划的&#xff0c;因此出于安全考虑&#xff0c;其可靠性至关重要。有源激光雷达传感器能够创建场景的精确3D表示&#xff0c;使其成为自动驾驶汽车环境感知的宝贵补充。由于光散射和遮挡&…

C++11学习笔记(3)——通用工具(上)(包含重要特性智能指针Smart pointer)

1.Pair 在C11中&#xff0c;std::pair是一个模板类&#xff0c;用于将两个值组合成一个单元。它可以将两个不同的类型的值配对在一起&#xff0c;并且提供了对这对值的访问和操作。 std::pair的定义 template<class T1, class T2> struct pair{T1 first;T2 second; };…

ElasticSearch完整入门及springboot集成

目录 ElasticSearch概述ElasticSearch版本特性Elasticsearch 5Elasticsearch 6.0Elasticsearch 7.0Elasticsearch 8.0 ElasticSearch安装Windowslinux Kibana安装Windowslinux使用 IK分词器(elasticsearch插件)安装自定义的词典 ES的基本使用字段数据类型索引操作文档操作创建文…

什么是oa系统,什么是工单系统,有啥区别?

什么是oa系统&#xff0c;什么是工单系统&#xff0c;有啥区别&#xff1f; 一、OA系统与工单系统介绍 1、什么是OA系统 OA系统全称为Office Automation&#xff0c;即办公自动化系统。它是一种专门为企业和机构的日常办公工作提供服务的综合性软件平台&#xff0c;具有信息…

apple pencil有买的必要吗?平板电容笔推荐平价

在当今时代&#xff0c;高科技已经成为推动电子产品发展的重要动力之一。无论是在工作上&#xff0c;还是在学习上&#xff0c;iPad平板的使用都很方便。iPad将会和我们的生活联系在一起&#xff0c;不管是现在还是未来。iPad配上一个简单的电容笔&#xff0c;不仅可以提高工作…

分享11个常用的VSCode快捷键,让你编码更高效

因为频繁切换到鼠标可能会对你的手腕造成不利影响。 说实话&#xff0c;快速编程是我继续编码的原因之一&#xff08;开个玩笑&#xff0c;除非...&#xff09;。无论如何&#xff0c;我发现让我变得更快的关键是与鼠标分离。想一想&#xff0c;每次你需要移动鼠标时&#xff0…

STM32单片机(六)TIM定时器 -> 第四节:TIM输出比较练习(PWM驱动LED呼吸灯、PWM驱动舵机以及PWM驱动直流电机)

❤️ 专栏简介&#xff1a;本专栏记录了从零学习单片机的过程&#xff0c;其中包括51单片机和STM32单片机两部分&#xff1b;建议先学习51单片机&#xff0c;其是STM32等高级单片机的基础&#xff1b;这样再学习STM32时才能融会贯通。 ☀️ 专栏适用人群 &#xff1a;适用于想要…