IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 开发工具 -> 【高易用性】【高可扩展性】【高可读性】统一返回体【抽象设计】 -> 正文阅读

[开发工具]【高易用性】【高可扩展性】【高可读性】统一返回体【抽象设计】

【统一返回体】抽象设计

背景

在分布式、微服务盛行的今天,绝大部分项目都采用的微服务框架与前后端分离方式。前端和后端进行交互,前端按照约定请求URL路径,并传入相关参数,后端服务器接收请求,进行业务处理,返回数据给前端。[1]1

维护一套完善且规范的接口是非常有必要的, 这样不仅能够提高对接效率,也可以让我的代码看起来更加简洁优雅。 [1]1

统一返回体是什么

统一返回体是一个包含了三个部分的数据 [2]2

  • code - 由后端统一定义各种返回结果的状态码
  • data - 本次返回的数据
  • message - 本次接口调用的结果描述

其中,codemessage 用于对后端处理的详情进行描述。

例如,前端发起 GET localhost:8080/the_most_handsome_person 请求(HTTP Request),后端返回以下响应体(HTTP Response Body):

{
    code: 0,
    data: {
        name: "张三",
        age: "20"
    },
    message: "请求成功"
}

统一返回体的作用

统一返回体是后端与前端开发人员进行约定的一个统一的返回格式

  • 有助于规范后端响应格式
  • 有助于前端处理请求响应

工程现状

Result<T>

当今工程实践中,统一返回体一般使用下方所示类进行建模:

  • 使用范型 <T> 进行数据类型抽象
  • 实现 Serializable 接口并为 serialVersionUID 设置固定值以实现序列化
  • 将构造器设计为 private 私有构造器,只能使用静态方法 Result.success(...)Result.error(…) 进行对象的创建
public class Result<T> implements Serializable {
    private static final Long serialVersionUID = 9192910608408209894L;

    private final T data;
    private final Integer code;
    private final String message;

    private Result(T data, Integer code, String message) {
        this.data = data;
        this.code = code;
        this.message = message;
    }

    public static <T> Result<T> success(T data, int code, String message) {
        return new Result<>(data, code, message);
    }

    public static <T> Result<T> error(T data, int code, String message) {
        return new Result<>(data, code, message);
    }
}

可以使用以下代码进行统一返回体的创建

class Person {
    private String name;
    private Integer age;
    
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    
    // 省略构造器、getter/setter、toString 等方法
}

@RestController
public class TestController {
    @GetMapping("/the_most_handsome_person")
 	public Result<?> theMostHandsomePerson() {
        Person zhangsan = new Person("张三", 20);
        return Result.success(zhangsan, 0, "请求成功");  /*  创建 Result<T> 对象  */
    }   
}

创建返回描述枚举类 ResultEnum

ResultEnum 枚举类有 codemessage 两个域字段,分别对应 Result 中的 codemessage 字段:

public enum ResultEnum {
    SUCCESS(0, "Success"), // 请求成功
    ERROR(1, "Error"); // 请求失败

    private final Integer code;

    private final String message;

    DefaultResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString() { ... }
}

此时,后端代码无需进行硬编码,可以使用枚举类修改为:

class Person {
    private String name;
    private Integer age;
    
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    
    // 省略构造器、getter/setter、toString 等方法
}

@RestController
public class TestController {
    @GetMapping("/the_most_handsome_person")
 	public Result<?> theMostHandsomePerson() {
        Person zhangsan = new Person("张三", 20);
        return Result.success(zhangsan, ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage());  /*  创建 Result<T> 对象  */
    }   
}

传统方法的缺点(问题)

在实际开发中,Result 类更多地是被放在共用的工具库中。

然而,每个不同的业务可能都有自己的状态码,甚至每个接口都可能会有(根据自己的设计),而每次调用 Result.success(...) 都会造成一段极为冗长的代码

最终解决方案 —— 抽象

抽取出 ResultEnumerable 接口

观察到每次代码调用都要调用枚举类型的 getCode()getMessage() 方法(例如 return Result.success(zhangsan, ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage());),故可以将这两个方法抽离成接口(extract interface

public interface ResultEnumerable {
    Integer getCode();

    String getMessage();

    @Override
    String toString();  /*  可选  */
}

返回枚举类 ResultEnum 实现 ResultEnumerable 接口

public enum ResultEnum implements ResultEnumerable {
    SUCCESS(0, "Success"), // 请求成功
    ERROR(1, "Error"); // 请求失败

    private final Integer code;

    private final String message;

    DefaultResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "ResultEnum{" +
                "code=" + code +
                ", message='" + message + '\'' +
                '}';
    }
}

Result 类增加多态方法

public class Result<T> implements Serializable {
    private static final Long serialVersionUID = 9192910608408209894L;
    
    private final T data;
    private final Integer code;
    private final String message;

    private Result(T data, Integer code, String message) {
        this.data = data;
        this.code = code;
        this.message = message;
    }

    // 增加方法,参数类型为 ResultEnumerable 接口
    public static <T> Result<T> success(T data, ResultEnumerable resultEnum) {
        return new Result<>(data, resultEnum.getCode(), resultEnum.getMessage());
    }

    public static <T> Result<T> success(T data, int code, String message) {
        return new Result<>(data, code, message);
    }
        
    // 增加方法,参数类型为 ResultEnumerable 接口
    public static <T> Result<T> error(T data, ResultEnumerable resultEnum) {
        return new Result<>(data, resultEnum.getCode(), resultEnum.getMessage());
    }

    public static <T> Result<T> error(T data, int code, String message) {
        return new Result<>(data, code, message);
    }
}

调用 Result.success(T, ResultEnumerable) 方法

此时,不需要再调用 Result.success(zhangsan, ResultEnum.SUCCESS.getCode(), ResultEnum.SUCCESS.getMessage());,只需要调用 Result.success(zhangsan, ResultEnum.SUCCESS) 即可:

class Person {
    private String name;
    private Integer age;
    
    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    
    // 省略构造器、getter/setter、toString 等方法
}

@RestController
public class TestController {
    @GetMapping("/the_most_handsome_person")
 	public Result<?> theMostHandsomePerson() {
        Person zhangsan = new Person("张三", 20);
        
        return Result.success(zhangsan, ResultEnum.SUCCESS); // 使用枚举即可
    }   
}

新实现的优点

易用性极高

在实际开发中,只需要将 Result 类与 ResultEnumerable 接口放在自己的项目代码中即可,无其他依赖。

可扩展性极高

在自己的实际业务中,只需要实现 ResultEnumerable 接口,就可以实现自己的枚举类!

public enum UserResultEnum implements ResultEnumerable {
    SUCCESS(0, "Success"), // 请求成功
    USER_NOT_FOUND(1, "User not found"), // 没有该用户
    USER_INFO_IS_PRIVATE(2, "User's information is private"); // 用户信息是隐私

    private final Integer code;

    private final String message;

    DefaultResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "ResultEnum{" +
                "code=" + code +
                ", message='" + message + '\'' +
                '}';
    }
}

可读性极高

命名清晰的枚举可以清晰地表述实际信息(例如 USER_NOT_FOUND):

import static com.example.demo.result.UserResultEnum.*;

@RestController
public class TestController {
    @GetMapping("/the_most_handsome_person")
 	public Result<?> theMostHandsomePerson() {
        Person zhangsan = new Person("张三", 20);
        
        // 代码内容:Result.success(zhangsan, SUCCESS)
        // 实际含义: 返回    成功     张三       成功
        // return Result.success(zhangsan, SUCCESS);
        
        // 代码内容:Result.error(null, USER_NOT_FOUND)
        // 实际含义: 返回    失败          没有该用户
        return Result.error(null, USER_NOT_FOUND);
    }   
}

完整的代码实现

Result

package cn.tzq0301.result;

import java.io.Serializable;

import static cn.tzq0301.result.DefaultResultEnum.*;

/**
 * @author tzq0301
 * @version 1.0
 */
public class Result<T> implements Serializable {

    private static final Long serialVersionUID = 9192910608408209894L;

    private final T data;

    private final Integer code;

    private final String message;

    private Result(T data, Integer code, String message) {
        this.data = data;
        this.code = code;
        this.message = message;
    }

    public static <T> Result<T> success() {
        return new Result<>(null, SUCCESS.getCode(), SUCCESS.getMessage());
    }

    public static <T> Result<T> success(T data) {
        return new Result<>(data, SUCCESS.getCode(), SUCCESS.getMessage());
    }

    public static <T> Result<T> success(int code, String message) {
        return new Result<>(null, code, message);
    }

    public static <T> Result<T> success(ResultEnumerable resultEnum) {
        return new Result<>(null, resultEnum.getCode(), resultEnum.getMessage());
    }

    public static <T> Result<T> success(T data, ResultEnumerable resultEnum) {
        return new Result<>(data, resultEnum.getCode(), resultEnum.getMessage());
    }

    public static <T> Result<T> success(T data, int code, String message) {
        return new Result<>(data, code, message);
    }

    public static <T> Result<T> error() {
        return new Result<>(null, ERROR.getCode(), ERROR.getMessage());
    }

    public static <T> Result<T> error(T data) {
        return new Result<>(data, ERROR.getCode(), ERROR.getMessage());
    }

    public static <T> Result<T> error(int code, String message) {
        return new Result<>(null, code, message);
    }

    public static <T> Result<T> error(ResultEnumerable resultEnum) {
        return new Result<>(null, resultEnum.getCode(), resultEnum.getMessage());
    }

    public static <T> Result<T> error(T data, ResultEnumerable resultEnum) {
        return new Result<>(data, resultEnum.getCode(), resultEnum.getMessage());
    }

    public static <T> Result<T> error(T data, int code, String message) {
        return new Result<>(data, code, message);
    }

    public T getData() {
        return data;
    }

    public Integer getCode() {
        return code;
    }

    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "Result{" +
                "data=" + data +
                ", code=" + code +
                ", message='" + message + '\'' +
                '}';
    }
}

ResultEnumerable 接口

package cn.tzq0301.result;

/**
 * @author tzq0301
 * @version 1.0
 */
public interface ResultEnumerable {
    Integer getCode();

    String getMessage();

    @Override
    String toString();
}

DefaultResultEnum

package cn.tzq0301.result;

/**
 * {@link Result} 的返回码与返回信息的枚举类
 *
 * @author tzq0301
 * @version 1.0
 */
public enum DefaultResultEnum implements ResultEnumerable {
    SUCCESS(0, "Success"), // 请求成功
    ERROR(1, "Error"); // 请求失败

    private final Integer code;

    private final String message;

    DefaultResultEnum(Integer code, String message) {
        this.code = code;
        this.message = message;
    }

    @Override
    public Integer getCode() {
        return code;
    }

    @Override
    public String getMessage() {
        return message;
    }

    @Override
    public String toString() {
        return "ResultEnum{" +
                "code=" + code +
                ", message='" + message + '\'' +
                '}';
    }
}

参考资料

  开发工具 最新文章
Postman接口测试之Mock快速入门
ASCII码空格替换查表_最全ASCII码对照表0-2
如何使用 ssh 建立 socks 代理
Typora配合PicGo阿里云图床配置
SoapUI、Jmeter、Postman三种接口测试工具的
github用相对路径显示图片_GitHub 中 readm
Windows编译g2o及其g2o viewer
解决jupyter notebook无法连接/ jupyter连接
Git恢复到之前版本
VScode常用快捷键
上一篇文章      下一篇文章      查看所有文章
加:2022-03-06 13:19:41  更:2022-03-06 13:20:51 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/26 6:32:04-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码