openapi-codegen 解决前后端联调最后一公里

1,078 阅读9分钟

一、前因后果

    不知道各位前端小伙伴是不是受够了在接口联调阶段对着文档挨个对字段,反正俺是受不了这么枯燥的事情。

    咱们程序员天生抵触这种需要大量沟通、消耗精力的事情,而且这种事更偏向体力劳动,没啥大的技术含量,做一天一个月一年还行,如果一辈子都要吭哧吭哧对接,岂不是名副其实的码农了。

    幸运的是,俺们业务后端提供了swagger api文档,而且是标准的OpenAPI2.0规范。

    各位看官可能觉得既然提供了标准易用的文档,前端对着做就已经非常前卫了,No No No,技术的提升永无止境。

    标准易用的文档有了,但我们还是要写很多的对接代码,比如要包装一下api、做数据转换,如果还想 typescript 友好的话需要自己对每个接口进行封装。如果能直接从文档变成代码就好了。

    接下来我们要做的一切文章都是围绕着OpenAPI规范文档这一大前提进行的。(ps:如果后端还没有提供swagger或者其他标准可解析的api文档,赶紧催催,或者架起BFF层自己做,参见@nestjs/swagger

二、API 代码生成的社区方案

    这里列举了几种社区和业界流行 API 代码生成的方案:

社区方案语言支持 代码生成方式 描述 
 Apifox(代码生成模块基于openapi-generator) 40多种 手动点击 Postman + Swagger + Mock + JMeter。主打文档维护功能,附带代码生成。
 swagger(swagger-codegen) 40多种 CLI命令,提供Jar包服务 依靠注解自动生成api文档、40多种语言支持、api文档可交互、丰富的社区工具。
 openapi-genreator 40多种 CLI命令,提供Docker或Jar包服务 从 OpenAPI 规范定义的文档生成客户端sdk、服务器sdk和文档。   fork 自 Swagger-codegen 仓库,至于为啥 fork,可以参见Why was it decided to fork Swagger Codegen?

    表格中列举的都是当前业界流行的方案,此外还有一些类似VSCode插件的工具,其功能与Apifox相差不大所以就不多做介绍。

    先说 Apifox 工具,他需要我们下载软件程序,然后按流程一步步点击生成代码到指定目录。这样做也不是不可以,但是,不要小看咱们程序员的懒惰!万一有一天需求迭代,在原有的api上做了一点调整,你的内心OS一定是:啊,小改动,就不生成了吧,ts咋报错了,反正能跑,不管啦,让我看看是ts-ignore还是--no-verify呢? 达咩,靠个人的自觉是不可能的,归根结底还是不够好用,不够贴切使用场景,我们的宗旨是能通过技术手段解决的问题就不要靠人。\

    然后是 swagger 和 openapi,需要用户自行下载 cli,通过配置文件加 shell 命令进行代码生成。咱们可以把 cli 命令与 npm script 脚本命令结合,比如启动项目时更新;缺点也比较明显代码生成时不仅大量占用 CPU 资源还需要频繁的磁盘 I/O,这谁受得了啊。另外,一个项目不可能只依赖一个后端服务,现在都在玩服务拆分,一个前端项目依赖七八个后端服务是很常见的事情,再说了一个后端服务也会对接到不同的前端项目,那版本管理岂不是想当混乱?再者,两个前端并行开发一个项目,上线前夕大家合并代码的时候发生了冲突,这时候可能就玩不下去了。

    我们先将当前所有的问题和期望梳理一遍:

问题和期望  描述与内心OS
 代码能自己生成 不要占用我的电脑资源
 本地开发时,代码生成后能自己更新 不要让我手动操作
 清晰的版本管理 解决:一个后端应用到多个前端,一个前端应用了多个后端,版本应该如何控制
 避免代码冲突 开发、测试、上线流程友好
 统一管理 有个中台系统,能帮我掌控全局

    这些问题肯定不止我一个人遇到,那么有没有现成的解决方案呢?经过我一个多月间歇性的调查后,很遗憾,没有。这轮子我造定了!!!

三、Openapi-Codegen 项目

3.1 项目介绍

Openapi-Codegen 项目是一个在 openapi-generator 基础上进行开发,针对业务场景,为前端开发人员定制的 server + cli 工具。其功能如下:

  1. 基于 openapi-generator;
  2. 在服务端进行代码生成,定时检查最新文档;
  3. 生成的代码文件自动同步 git,自动发布 npm 包;
  4. 使用方在项目中安装 CLI 工具, 在本地自动更新依赖或文件;

    总的来说,就是把代码生成工作放到服务端进行,生成后自动同步到 git,完成 npm 包发布,还能定时检查是否有最新的 OpenAPI 文档;在项目中使用时需要安装对应的依赖包,或者以 submodule 的方式使用,同时提供了 cli 工具来帮助用户自动更新项目中的代码包。

    是不是感觉很炫酷。

3.2 如何生成代码?

  1. 添加配置文件(因为 json 数据更适合做展示,其实这里还可以通过页面表单操作);

\

  1. 如果可以的话,请后端同学帮忙,当他部署机器时,调用一下咱们提供的api hook,触发代码生成;

  2. 还做了定时任务去抓取服务机器,来触发代码生成(这里需要提供目标机器IP的清单)。

3.3 代码包的使用

简单的服务分层概念:在 JAVA 服务中,一个业务模块通常由 Route、Controller、Service、Api 四层组成。其中 Route 与 Controller 通常存在某种对应关系,简化一下就是 route 对应 Controller 中的某个 method 方法。

    下图中通过一个 api 简单的示意自动生成的代码包如何使用:

    第一步(line:3)从包中导出需要的业务模块 Controller 和 配置选项;

    第二步(line:5)通过配置定义所有请求的公共前缀,或者其他;

    第三步(line:7~line:13)实例化 Controller 对象,最后调用 controller.method 拿到结果。从图中我们很容易看到响应结果里面有完备的 ts 类型提示,是不是很友好。

    其他:不仅仅是响应结果,入参也有,你甚至可以从包中导出自己需要的类型,应用到项目,参见图中第一行。

\

    下图中是一般情况下的接口定义,我相信各位看官心中肯定疑惑,明明是下面的更加简单,我在这里搁着搁那的搞啥名堂?!的却,直观感受上确实会觉得下面这种更简单。但是,请再仔细瞧瞧,下面这种缺少了typescript类型支持,无论是入参还是响应结果,都需要开发者自己去定义类型,如果加上类型定义的话,代码量瞬间超过前者。当然,类型定义不是强求的,开发者也可以不写,但我们做技术的不就是要追求极致吗,既然项目工程用上了 ts,为什么不贯彻到底呢?“代码即文档”才是我们的终极理想。

\

3.4 项目设计

3.4.1  服务端生成

    把代码生成放到服务端,使得我们能够统一管理代码包,自动更新也变得十分容易。\

    项目分为服务加CLI工具,服务侧总共四个模块,生成模块、配置模块、Socket通信、控制台页。

  1. 生成模块负责输出代码包、同步git仓库、发布npm包;

  2. 配置模块负责管理 git 权限、npm 权限、抓取后端服务文档以及代码生成的配置项;\

  3. Socket 模块负责与 CLI 工具通信交互,自动化管理前端项目的包版本;

  4. 控制台页面负责展示所有代码包的情况,包括版本记录、更新内容,还可以在控制台手动触发更新或升级版本号。

\

3.4.2  版本管理思路(npm dist-tag)

    codegen项目的核心思路除了把代码生成放到服务端之外,另外一个就是代码版本的管理思路。\

    我们前端项目在安装一个依赖包的时候,一般都会执行 npm install openapi,这其实是安装了 openapi 的 latest 版本,相当于 npm install openapi@latest,但是我的思路是放弃 latest 版本,使用项目维度的 dist-tag。\

    具体的,假设现在有个已经生成好的代码包叫做 openapi,两个前端项目仓库分别是 property 和  customer。我要求代码包也有两个 dist-tag 分别对应两个前端项目,使前端项目只需要在 package.json 里面恒定的依赖一个属于自己的 dist-tag 号,上线时只需要修改 dist-tag 指向的版本即可。

安装代码包 前端项目(dist-tag号) 真实版本
npm install openapi@latest弃之不用 v1.4.0
npm install openapi@property property v1.3.1
npm install openapi@customercustomer v0.2.0

3.5 可靠性

    可能大家内心会对自动生成的代码包存有疑虑,担心它运行时报错或者与期望不符。这里我给出四个点来供各位参考:

  1. openapi-generator 要求文档必须经过标准规范的校验后才会生成代码,否则生成失败;

  2. 在本项目中,生成的代码会利用 typescript 的 tsc 进行类型检查,检查通过后才会升级版本,进行后续操作;\

  3. 生成的代码都是“在模板中填空”得到的,所以除了请求地址外,其他的代码逻辑大都一致。不同之处主要聚集在 TS 类型上,所以就算类型出错,也不用担心运行逻辑出问题;

  4. CLI 工具提供一个基于 Jest 开发的简单的单元测试用例,该用例会调用每个 api 检查其能够正常发起请求,发起请求的地址与文档中是否一致。

    总的来说,我个人浅薄的认为,还是可以放心大胆的拥抱一下未来。\

3.6 有啥好处?

    目前已经在内部落地到三个业务项目,使用体验非常优秀。

    对于开发者来说,再也不用去做的事情有:api 封装、对照文档按图索骥、接口请求使用 any 一把嗦。

    因为 api 获得了完善的ts类型,还会倒逼项目整体的风格从 “any-script” 升级到真正的 typescript,让我们的项目更加健壮。

\

参考:

[1]  openapi-generator 项目地址:github.com/OpenAPITool…

[2]  openapi-generator 是用说明之支持的语言列表:openapi-generator.tech/docs/genera…

\