Laravel 项目全自动接口管理(接口部分)

简介

本工具旨在通过一个包含简单完备的接口描述,自动生成接口校验、数据字典和其对应文档
本工具包含接口管理和回归测试两部分功能,本文只覆盖接口管理功能。

运作方式

输入 - 模板

输出 - 接口代码、数据字典代码、接口文档、postman文件、客户端代码(暂未支持)、异常响应(i18n暂未支持)

已支持数据类型

标量:Integer, String
枚举:任意自定义的枚举(字符串 / 数字)
模型:包含多个字段的组合类型
数组:同级同结构的数据可以描述为数组
接口:接口类似于模型,但数据需要被标记为Request或Response,用于区分是输入数据还是输出数据。
异常码:全局异常码,可以在接口任意执行阶段抛出,终止执行并返回响应。

示例

接口

假设我们需要一个获取用户历史记录的接口。
该接口使用auth中间件,请求POST,地址为http://{{host}}/api/v1/xxxxx/get_class_histories
请求参数为年月和分页的页数,都为可选参数,响应为ClassRecord的数组。

        class Xxxxx {
            /**
             * 获取历史记录,不限制时间区间
             * 先搜年月,后根据时间倒序获取分页
             */
            @Middleware("auth")
            @API(methods = {MethodType.POST})
            class GetClassHistories {
                /** 不填默认本年 */
                @Request
                @Optional
                Integer year;
                /** 不填默认本月 */
                @Request
                @Optional
                String month;
                /**
                 * 第几页
                 */
                @Request
                @Optional
                Integer page;
                @Response
                Models.ClassRecord[] histories;
            }
        }

模型

在上面的接口中提到了ClassRecord模型
下面为其数据描述,包含多个数据字段,既有标量类型也有枚举和模型。
其中的Optional注解表示该字段可以为null,其他模型略。

        class ClassRecord {
            Integer id;
            /** 注释 */
            @Optional
            Integer sid;
            Integer pay;
            Integer useTool;
            /** 有时候需要用它搜索 */
            Integer beginTime;
            /** Y-m-d H:i:s */
            String datetimeOfBeginTime;
            /** 开始时间周几 */
            Enums.DayOfWeek dayOfBeginTime;
            /**
             * 注释
             */
            @Optional
            Enums.ClassRecordFreeTry freeTry;
            /** 状态 */
            Enums.ClassRecordStatus status;
            @Optional
            Models.Material material;
            /** 评价,可以为空 */
            @Optional
            Models.ClassComment classComment;
            @Optional
            Enums.ClassType classType;

            @Optional
            Models.Teacher teacher;
        }

枚举

从0开始自增的枚举数据

    /** 周几,从周日开始 */
    enum DayOfWeek {
        SUNDAY,
        MONDAY,
        TWESDAY,
        WEDNESDAY,
        THURSDAY,
        FRIDAY,
        SATURDAY,
    }

手动指定了value的int枚举

    /**
     * 用户状态
     */
    enum UserStatus implements FixedEnumValueInterface {
        INIT(0),
        DISABLED(2),
        IN_CLASS(4),
        ;
        int value;

        UserStatus(int value) {
            this.value = value;
        }

        @Override
        public int getValue() {
            return value;
        }
    }

全局错误码

    /** 服务器内部错误,等同于500 */
    SERVER_INTERNAL_ERROR(-1),
    /** 参数不合法 */
    INVALID_PARAMETER(-2),
    /** 接口已失效,应校验是否是该版本接口已禁用,若已禁用客户端应触发升级提示 */
    API_DEPRECATED(-10),
    /** 等同于404 */
    API_NOT_FOUND(-11),
    /** 维护模式 */
    MAINTAIN_MODE(-20),
    /** 用户未登录 */
    AUTH_FAILED(-100),
    /** 自定义的ERROR信息 */
    CUSTOM_ERROR_MESSAGE(-10000),

依赖

JDK8
php 7.2
laravel 5.7
larecipe
php-parser
由于未发布正式版,请使用composer指定github引入
{
"type": "vcs",
"url": "https://github.com/Kamicloud/api-generator..."
}
建议使用vscode并安装java lang support扩展,他可以提供模板的hint

结果

配置JAVA_HOME环境变量,指向安装的jdk
file
引入工具和larecipe
执行
php artisan larecipe:install
php artisan generator:install
将所需的文件发布到当前项目中
可以看到resources下generator出现templates、config、defines三个文件夹
templates即接口描述目录,参考已存在的描述设计自己的接口
config中为配置信息,建议使用默认配置

app/bin下是发布后的命令
initGenerator会初始模板解析环境,如果使用composer update进行工具更新,需要重新执行该命令
generate将根据模板生成代码和文档

localhost/docs下为文档目录
app/Http/Services/下为业务代码目录,在$message中获取对应的请求数据和setResponse
$message->setResponse(xxxxxModel::initFromEloquents($xxxx));

接口文档
file
file
生成的代码
file
生成的测试用例文件


# __api: /api/api/v1/xxxxxxxxxx/get_class_histories
__enabled: true
__role: User
__user: 1
__anchor:
__params:
__testcases:
  -
    __params:
      year:
      month:
      page:
  -
    __params:
      year: 2019
      month: 3
      page:
  -
    __params:
      year: 2018
      month: 12
      page:

使用生成的postman调试接口
file
从Eloquent模型到DTO
\App\Generated\xxxx\xxxModel::initFromEloquent($eloquentModel)
如果对应的key不相同,可以使用@DBField("name")指定映射,如果不指定,默认指向low dash的字段

结语

目前还没想好如何在客户端同步调用接口的代码,所以暂时不支持生成客户端代码。而且服务端在开发时会建立独立的分支,如何让客户端方便的同步其他分支的代码也是要考虑的地方。
本工具还在开发中,募集贡献者,发布正式版前,不建议应用于生产环境

本作品采用《CC 协议》,转载必须注明作者和本文链接
为码农摸鱼事业而奋斗
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 6

@zhaomengqiao

比较过openapi和dingo,无论是使用yaml还是php doc,都没有办法做到简洁和准确。
特别是swagger,我用官网的demo复制到vscode,yaml的缩进全都乱了,而且一个接口的描述需要用好几行来完成。
而强类型的编程语言就不会有这方面问题。在做这个工具的时候,有人已经做了一个功能很接近的闭源程序,某种程度上说,也是用java做模板这条路走得通。
也考虑过typescript/python/php,不过因为不支持类嵌套,模板会很丑。
对于java方面,其实已经弱化了他的存在,暴露给使用者的模板完全可以看做单纯的文本。使用的时候也只是配下环境执行脚本,编译和执行我都封装起来了。
另外只使用java做代码生成,校验之类的功能还是在各端代码的基类完成的。

5年前 评论
Rming

golang呢?对java无好感……

5年前 评论

@Rming golang做模板不好看啊,而且golang好像没有装饰器/注解这类语法,对于需要描述变量为nullable或者mutable应该会很难做

5年前 评论

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!