Java Spring Boot 规范统一响应体结构

在进行 web开发 中,如果我们的返回数据不统一,会是啥样呢,比如像下面这种:

java 复制代码
@RestController
public class DemoController {
    @GetMapping("/haha")
    public Object haha() {
        return "";
    }

    @GetMapping("/heihei")
    public Object heihei() {
        Map<String, Object> data = new HashMap<>();
        data.put("one", 1);
        return data;
    }

}

如果返回数据格式不统一,对前后端的数据交互不太友好。另外为了代码更加规范、好维护,我们通常都采取统一的响应体结构。

通常来说不可缺少的三要素:

  • code,响应码,标识不同响应
  • message,响应信息,作为描述性的
  • data,如果有传输的数据,将数据放在data中

有的人会说了,code响应码不是有 httpStatusCode 吗,为何还要指定自己的 code 的呢?其实,这里主要是为了更好地区分不同的响应数据,比如同样的 200 状态码,只要后台正常运行,你能知道到底啥问题吗,但是如果我们限定 0表示成功,非0表示失败,甚至更加具体的如传输文件过大,字段不合规,数据库连接超时等,同样 状态码都是 200 就很难知道到底是什么问题。

为了更加统一,我们通过 枚举 的方式限定 成功、失败 的具体 code和msg,然后在 统一的响应体的规定结构,再写几个不同参数的响应方法,基本就够用了,如果有新的错误等,我们也可以在 枚举 中再添加,大神请勿喷。

接下来我们按照 枚举和响应体类及示例的顺序,演示规范处理。

为了减少代码的书写,我们引入 lombok,依赖如下:

xml 复制代码
<!-- Lombok 工具 -->
<!-- @Data && slf4j -->
<dependency>
    <groupId>org.projectlombok</groupId>
    <artifactId>lombok</artifactId>
    <optional>true</optional>
</dependency>

code与msg的枚举

java 复制代码
package com.example.springbootuniformrespandexceptionhandle.params;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public enum RespCodeEnums {
    SUCCESS(0, "Success"),
    FAIL(1, "Operation failed."),

    FILE_OVER_SIZE(1000, "File over size."),
    FILE_FORMAT_WRONG(1001, "File format is wrong."),
    PARAM_ERROR(1003, "Request param is wrong."),
    JSON_PARSE_ERROR(1004, "Json parse error."),
    UNAUTHENTICATION_ERROR(1100, "Not authenticate."),
    UNAUTHORIZED_ERROR(1101, "Not Authorized."),
    REQUEST_METHOD_ERROR(1201, "Request method wrong."),
    API_NOT_EXIST(1301, "Api not exist."),
    TIMESTAMP_ERROR(1401, "Timestamp wrong."),
    DATA_NOT_EXIST(1501, "Data not exist."),

    REDIS_CONN_ERROR(2001, "Redis Conn wrong."),
    REDIS_OPER_ERROR(2002, "Redis operate wrong."),
    MYSQL_CONN_ERROR(2101, "MySQL Conn wrong."),
    MYSQL_QUERY_TIMEOUT(2102, "MySQL Query timeout."),
    MYSQL_DATA_NOT_EXIST(2103, "MySQl Cannot find data."),
    ELASTICSEARCH_CONN_ERROR(2201, "Redis Conn wrong."),
    ELASTICSEARCH_QUERY_TIMEOUT(2202, "Elasticsearch query timeout.");

    private int code;

    private String msg;
}

上面的 枚举 只是很小的一部分,实际的项目开发具体分析,这部分具体内容请结合自己项目编码。

封装统一的响应体

java 复制代码
package com.example.springbootuniformrespandexceptionhandle.params;

import lombok.Data;

@Data
public class RespInfo<T> {
    private int code;

    private String msg;

    private T data;

    /**
     * 全参方法
     * @param code
     * @param msg
     * @param data
     * @param <T>
     * @return
     */
    private static <T> RespInfo<T> response(int code, String msg, T data) {
        RespInfo<T> respInfo = new RespInfo<>();
        respInfo.setCode(code);
        respInfo.setMsg(msg);
        respInfo.setData(data);

        return respInfo;
    }

    /**
     * 缺失data方法
     * @param code
     * @param msg
     * @param <T>
     * @return
     */
    private static <T> RespInfo<T> response(int code, String msg) {
        RespInfo<T> respInfo = new RespInfo<>();
        respInfo.setCode(code);
        respInfo.setMsg(msg);

        return respInfo;
    }

    public static <T> RespInfo<T> success() {
        return response(RespCodeEnums.SUCCESS.getCode(), RespCodeEnums.SUCCESS.getMsg());
    }

    public static <T> RespInfo<T> success(T data) {
        return response(RespCodeEnums.SUCCESS.getCode(), RespCodeEnums.SUCCESS.getMsg(), data);
    }

    public static <T> RespInfo<T> fail() {
        return response(RespCodeEnums.FAIL.getCode(), RespCodeEnums.FAIL.getMsg());
    }

    public static <T> RespInfo<T> fail(int code, String msg) {
        return response(code, msg);
    }

    public static <T> RespInfo<T> fail(int code, String msg, T data) {
        return response(code, msg, data);
    }
}

我们主要限定三个字段,code、msg、data,其中 data 具体的数据结构我们不太清楚,所以这里引入泛型,在实际的项目开发中,这个也很常见。

另外,我们实现了 成功返回的默认、带数据的成功返回,失败返回默认。待具体描述的失败返回等,这些看下代码就一目了然。

示例

这里我们实现一个简单的用户信息管理的控制器类:

kotlin 复制代码
package com.example.springbootuniformrespandexceptionhandle.controller;

import com.example.springbootuniformrespandexceptionhandle.model.User;
import com.example.springbootuniformrespandexceptionhandle.params.RespCodeEnums;
import com.example.springbootuniformrespandexceptionhandle.params.RespInfo;
import org.springframework.web.bind.annotation.*;

import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping("/user")
public class UserController {
    // 存在于运行内存中
    private static Map<String, Object> users = new ConcurrentHashMap<>();

    @PostMapping("/add")
    public RespInfo addUser(@RequestBody User user) {
        users.put(user.getName(), user);

        return RespInfo.success();
    }

    @PostMapping("/info")
    public RespInfo getInfo(@RequestBody User user) {
        if (!users.containsKey(user.getName())) {
            return RespInfo.fail(RespCodeEnums.DATA_NOT_EXIST.getCode(), RespCodeEnums.DATA_NOT_EXIST.getMsg());
        }

        return RespInfo.success(users.get(user.getName()));
    }

    @GetMapping("/allUsers")
    public RespInfo getAllInfo() {
        return RespInfo.success(users);
    }

}

可以添加用户,查询某个用户信息,查询所有用户信息,相关的用户 bean如下:

java 复制代码
package com.example.springbootuniformrespandexceptionhandle.model;

import lombok.Data;

@Data
public class User {
    private String name;

    private int age;

    private String phone;
}

以下是测试情况:

未查询到数据

查询到数据

获取所有数据

相关推荐
devlei2 小时前
从源码泄露看AI Agent未来:深度对比Claude Code原生实现与OpenClaw开源方案
android·前端·后端
努力的小郑3 小时前
Canal 不难,难的是用好:从接入到治理
后端·mysql·性能优化
Victor3564 小时前
MongoDB(87)如何使用GridFS?
后端
Victor3564 小时前
MongoDB(88)如何进行数据迁移?
后端
小红的布丁4 小时前
单线程 Redis 的高性能之道
redis·后端
GetcharZp4 小时前
Go 语言只能写后端?这款 2D 游戏引擎刷新你的认知!
后端
宁瑶琴6 小时前
COBOL语言的云计算
开发语言·后端·golang
普通网友6 小时前
阿里云国际版服务器,真的是学生党的性价比之选吗?
后端·python·阿里云·flask·云计算
IT_陈寒7 小时前
Vue的这个响应式问题,坑了我整整两小时
前端·人工智能·后端
Soofjan8 小时前
Go 内存回收-GC 源码1-触发与阶段
后端