likes
comments
collection
share

优雅地设计一个Restful响应体类(结构体)——R

作者站长头像
站长
· 阅读数 18

简介

今天给大家介绍一下如何设计一个业务数据响应体类,就是前端每次请求后端返回的数据我们要统一数据结构, 不能想怎么写就怎么写。规定一个通用的后端数据响应体类十分必要。

响应体类R

其实也很简单,只需要声明一个类,随便你叫什么。RResultRestResponse 都行啦,我这边为了简洁声明为了 R 类。

R 类有 3 个属性,分别是:

  • code:Integer 类型,业务状态码,每种错误都对应着一个业务错误码或者状态码,由项目团队自定义。
  • msg:String 类型,给前端返回的消息,提示文字。
  • data:类型是泛型类 T,用于保存要返回的数据,根据最终要返回的数据类型而定。比如说要返回 List<String>,那么最终 Controller 层方法返回值就可以写成 R<List<String>>

R 类中还定义了一些常量,比如说提示消息,业务状态码,这个可以根据自身的业务而定。每家公司的业务标准都不太一样。

@Data
public class R<T> {

    private Integer code = NORMAL_CODE;

    private String msg = "";

    private T data;

    public static final String SUCCESS = "操作成功";

    public static final String FAILURE = "操作失败";
    
    public static final Integer ERROR_CODE = 500;
    
    public static final Integer NORMAL_CODE = 200;

    public R() {

    }

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

    public R(Integer code, String msg, T data) {
        this.code = code;
        this.msg = msg;
        this.data = data;
    }

    public static <T> R<T> ok() {
        return ok(HttpStatus.HTTP_OK, SUCCESS, null);
    }

    public static <T> R<T> ok(Integer code, String msg, T data) {
        R<T> r = new R<>();
        r.setCode(code);
        r.setMsg(msg);
        r.setData(data);
        return r;
    }

    public static <T> R<T> ok(T data) {
        return ok(ERROR_CODE, SUCCESS, data);
    }

    public static <T> R<T> fail(Integer code, String msg) {
        return ok(code, msg, null);
    }

    public static <T> R<T> fail(String msg) {
        return ok(ERROR_CODE, msg, null);
    }

    public static <T> R<T> fail() {
        return ok(ERROR_CODE, FAILURE, null);
    }
}

接口示例代码

下面演示一下接口的示例代码,以 UserController 为例,声明一个 UserController 类,标记为 @RestController

@RestController
@RequestMapping("/user")
public class UserController {

    private UserService userService;
    
    // 构造器注入
    @Autowired
    public UserController(UserService userService) {
        this.userService = userService;
    }
    
    @GetMapping("/list")
    public R<List<User>> selectAll() {
        List<User> list = userService.selectAll();
        return R.ok(list);
    }

}

最后响应的结果应该是这样:

{
    code: 200,
    msg: "操作成功",
    data: [
        {
            id: 1,
            username: "特朗普",
            age: 80
        },
        // ...
    ]
}

前端一般这样处理获得数据:

// 导入封装的查询接口
import { getUserListAPI } from '@/api/user.js'

async function getUserList() {
    // 结构出 data 字段,axios 需要在后置处理器配置直接返回 AxiosResponse 的 data 字段
    // 这样我们才能拿到我们自己定义的响应体,最后解构出我们自己定义的响应体的 data 字段
    // 如果担心名称重复可以使用 : 符号重命名
    const { data } = await getUserListAPI()
    
    // 赋值之后,自动触发组件刷新
    userList.value = data
}

其他语言版本的

如果你的后端使用了 Go 开发,那么可以如法炮制。定义一个结构体 R

type R[T any] struct {
    Code int    `json:code`
    Msg  string `json:msg`
    Data T      `json:data`
}

现在 Go 里面也可以使用泛型了,泛型类型约束为 any,也就是任意类型。Go 语言中不存在什么静态方法,可以自定义函数,但是大可不必,Go 语言的结构体使用起来就是强调一个简洁,跟 JS 的对象字面写法很类似。

下面演示一下在 Gin 框架里面使用响应体结构体 R

type UserController struct {
    userService *UserService
}

// wire 构造器注入
// 设置 Gin 的路由不作赘述
func NewUserController(userService UserService) *UserController {
    return &UserController{userService: &userService}
}

func (c *UserController) getUserList(ctx *gin.Context) {
    // 返回 User 的切片
    users := c.userService.getUserList()
    r := R[User[]]{
        code: 200,
        msg: "操作成功",
        data: users
    }
    ctx.JSON(200, r)
}

怎么样,你学会了吗?😁😁😁

转载自:https://juejin.cn/post/7374403519065030656
评论
请登录