AngelLover0 2019-06-21
团队内部RestAPI开发采用设计驱动开发的模式,即使用API设计文档解耦前端和后端的开发过程,双方只在联调与测试时耦合。在实际开发和与前端合作的过程中,受限于众多因素的影响,开发效率还有进一步提高的空间。本文的目的是优化工具链支持,减少一部分重复和枯燥的劳动。
根据个人的开发经验,后端编写API设计文档时常见的情况有:如果是简单的需求,API数量较少,后端直接通过内部即时通信软件和前端沟通;如果是复杂的需求,API数量较多,后端会先把API描述写到本地临时文档(纯文本、markdown、evernote等)或者内网(内部个人Wiki、git仓库)中,然后把链接发给前端review或者直接面对面沟通。这样的方式灵活,但存在一些问题,比如:
描述格式没有标准。对于简单的描述,文档格式比较随意,双方基于约定和经验理解和开发1;完备的描述,编写文档所需时间较长,并且细节复杂(需要考虑不同的HTTP请求类型、HTTP头部信息、HTTP请求内容等),高质量地创建这份文档本身就是件非常吃力的事,下游的抱怨声不绝于耳。当然在合作开发中,文档越完备,双方的理解偏差就越少、开发产生的bug就越少,后期也更容易维护代码、适应人员变更,但是编写完备的文档所需要的额外时间也不容忽视,没有代码产出的设计文档可能不得已让位于现实中整体开发时间的紧张。
以开发“获得管理员账户下可用商户”为例。如果是简单的描述,后端告知前端url为{host}/ajax/shop
,返回的结构是[{"shopId":int,"shopName":string}]
,有经验的前端会自动判断出Method
为Get
,Content-type
为application/json
,request不需要附带参数,不需要对错误值做特殊处理;而如果是复杂的描述,后端一般会列出API名称、功能描述、调用方式、请求参数、请求示例、返回值、成功的返回结果示例、失败的返回结果示例中的几项,填充到已有的API模板中2。
输入效率不高。由于开发的API模板缺乏固定的标准,因此只能在例如Wiki、纯文本编辑器、markdown编辑器中编写,无法得到现代IDE中语法高亮、自动补全、错误提示等特性的支持,整体感觉就像是在记事本中写Java。
> 举例:需求要求开发一个新增优惠券API,其样例数据只能由开发手动生成。如果是修改已有的API,要补充新的样例数据,开发一般会登录商户平台,打开优惠券页面,在Chrome中实际操作一遍,抓包得到request的body(json格式),在json格式化网站(如[json.cn](json.cn))美化后复制到API设计文档中。![clipboard.png](/img/bVTWta)
重复录入。因为文档库功能羸弱,使用不便,所以开发一般先按自己的格式写一份文档,但是如果不直接把API录入到公司文档库,则开发需要对一份API出两份设计文档。开发一般会开两个窗口,左边是API设计文档的完成件,右边是公司API文档库编辑页面,然后把左边格式各异的API描述文本转换到右边统一的markdown格式。
例如:想象一下从Wiki文档的表格中一个个复制粘贴,再编辑成markdown格式文本是典型的成本大于收益的工作。
文档维护成本大。由于文档和代码分开存放,由于需要手动操作,因此文档与代码同步成本较高。随着时间推移,不断修改接口实现的时候都必须同步修改接口文档,而文档与代码又处于两个不同的媒介,除非有严格的管理机制,不然很容易导致不一致现象,并在业务整体交接、开发成员替换时使后来人付出较大的时间成本。
不同的存放形式的优缺点见仁见智,类似于Spring也有XML和JavaConfig两种配置方式。
缺少样例数据。由于团队内部前端一般不会全面的了解业务,后端提供的样例数据往往比前端自己生成的Mock数据对业务需求的把握更准确。如果后端能在API设计文档中提供样例数据,一是如果前端没有自动Mock工具的话,能节约前端生成Mock数据的时间;二是能在联调前为前端提前发现一些低级错误(比如具有业务特征的一些默认值处理、空值处理、字段缺失等场景)。
beta环境绑定了唯一的beta域名,因此在多分支并行开发时是稀缺资源,较大的项目在beta环境编译和部署往往消耗很多等待和解决冲突的时间。如果在联调中发现的问题较多,就需要多次部署beta环境,时间成本十分可观。
如何减少部署时间另外行文
总结起来,上面列出的问题大部分是由于API描述标准不统一引起的,因此要用标准化的工厂代替散乱的手工生产。虽然平时开发的API具有Rest风格、对外网开放,只被企业自己的应用调用,不过普遍的WebAPI开发流程还是适用的。我在网上搜索一些功能较为符合的RestAPI设计工具,将其大致分为3类讨论。
人和机器可读的API描述标准,围绕该语言有完善的工具链:一般有设计、编译(即Codegen)、测试(有MockServer、自动Mock、本地直连等形式)、文档(包括静态文档,如html和pdf;还有可交互文档html+js)、合作(多人+多角色合作开发)这几个模块,各个标准都差不多。
较为学术性的表述:虽然Web API的实现正变得越来越普及,但在工具方面还缺乏一些被广泛接受的标准,用以描述、发现,并且理解大量基于API的服务的意义。Web API之“元语言”有三个关键领域:API描述、API发现以及API档案。所谓的API描述,指的是以一种让人类与机器都可读的形式对API进行描述,包括API的实现细节,例如资源与URL、表述格式(HTML、XML、JSON等等)、状态码以及输入参数。
Swagger、Apiary、RAML的格式各自采取了一种略有不同的设计方式,但在本质上都提供了相同的基本特性:以多种不同级别的细节对Web API进行描述。
以Swagger23为例,分为5个部分(示例图来自于RAML,不过功能都差不多)。
OpenAPI
(前身是Swagger API Spec
),提供强大的在线编辑功能,包括语法高亮、错误提示、自动完成、实时预览4,并且支持用户以Json、Yaml格式撰写5、导入、导出、转换文档。Spring Boot
构建,生成的代码包括定义的API接口、空实现方法的样板代码、业务POJO、配套的Swagger注解。值得注意的是,由自动生成的Swagger注解,可以反向生成最初的API设计文档SwaggerHub
中提供收费的在线测试功能,主要有MockServer(Auto Mocking
)、问题跟踪(Issue Tracking
)SwaggerHub
可以配合API版本,自动同步相应文档的版本SwaggerHub
提供团队管理、联调开发、文档标注等多人合作开发的支持再提一下Apiary和RAML。Apiary6使用API Blueprint
标准,Apiary网站提供了在线编辑、实时预览、Mock、可交互文档、团队合作、Github同步、流量追踪等包含整个API生命周期的所有服务,当然这是收费产品,而且价格不菲;另外,用户也可以通过开源的命令行工具进行离线的API设计、文档生成、发布过程,并将其集成到自己的工作流中,这也是它的一大特点。RAML使用RAML1.0
标准,没有自己的可视化在线开发平台,而是用官方或第三方的离线工具(如API Workbench
系列)来代替,因此它也存在一些缺点,比如:工具更新不及时,某些Tool不支持最新的RAML1.0
。
类似于Intellij Idea的生成JavaDoc
功能,是一种注释解析器,从C++、Java、Python代码注释中基于特定的关键字(如@param
、@return
)生成API静态文档。由于更像是先代码实现后生成API文档,所以不能算作是设计驱动的开发;另外apidocjs也缺乏IDE支持。
没有公开的API设计语言,提供在线或离线、闭源或开源的可视化、一体化API开发平台。这里选择中文的Rap、eolinker作为代表。Rap是阿里的开源作品,也提供线上服务,核心功能是文档编辑和自动Mock服务。eolinker是综合的接口管理平台,除了常见的功能,还提供接口商店、数据字典等适合创业团队快速开发API的特性。在此不做进一步介绍。
Swagger2 | API Blueprint | RAML | |
---|---|---|---|
Design | 在线编辑、IntelliJ Idea插件 | 在线编辑、命令行、Sublime/Atom/Vim插件 | API Workbench、Sublime/VS插件 |
Design文档格式 | yaml、json | markdown | yaml |
Build支持 | 在线Build、IntelliJ Idea插件 | / | Maven插件 |
Codegen服务端框架 | Spring Boot | / | JAX—RS |
Test | 运行时手动Mock、第三方工具 | 官方和第三方工具生成MockServer/Client | 第三方工具和在线服务 |
Document | Maven插件生成静态文档、在线或运行时生成可交互文档,支持SpringMVC+注解形式 | 第三方工具 | 第三方工具 |
Share | 在线、收费 | 在线、收费 | 离线、第三方工具 |
综合考虑,最后选择Swagger2。因为Swagger对现有的工作流侵入较少;工具较为完整;与团队使用的Spring MVC
技术栈无缝集成,可以减轻文档工作量。Swagger2也有一些缺点,如:使用注解方式对代码有侵入性。
减少文档的编写时间
OpenAPI
规定的关键字。Controller
类和相关方法上(以Spring MVC
和Spring Boot
为例),即可以通过Java反射在Maven Complie
或运行时生成API设计文档。Swagger有Intellij Idea
的插件支持,Swagger注解则能利用现代Java IDE的特性,提高输入效率;另外完善的注解也方便其他开发人员进行后期维护,不需要在设计文档和代码实现中来回切换查看。此种方式相当于面向规约的开发模式,即先规定接口,再填充实现。API Blueprint
的markdown格式可以存储到公司的API文档库,html静态文档可以存储到内部Wiki。Maven + Spring Boot
的服务端代码,不过生成的POJO和Controller类的命名可能不太理想,需要自己调整。Spring MVC
)先建立RestController类、相应的API空方法、POJO作为骨架。对应的API设计文档见文末的Reference
节。
@Api("Users") @RestController @RequestMapping(value = "/users") public class UserController { @ApiOperation(value = "创建用户", notes = "根据User对象创建用户") @PostMapping public String postUser(@RequestBody User user) { return null; } @ApiOperation(value = "获取用户详细信息", notes = "根据url的id来获取用户详细信息") @GetMapping("/{id}") public User getUser(@PathVariable Long id) { return null; } } class User{ private Long id; private String name; private String age; //getter,setter }
生成的具体方式按照耗时长短排列为:Maven Complie
、Test Case、Server Runtime。可在Swagger Editor中预览相应的可交互文档。根据前端的反馈,修改Swagger注解,并把新的文档存储到内部Wiki或者API文档库(如果改动量大的话,利用Diff工具提高效率)。
开发完成后启动Server,Swagger-UI的访问地址为http://localhost:8080/swagger-ui.html
为了减少beta环境的冲突、加快部署速度,最好在本地开发环境联调。
【5分钟指南】Swagger2环境配置与使用
swagger: '2.0' info: description: Click Link Below for Help version: v1 title: demo13 termsOfService: 'http://www.github.com/kongchen/swagger-maven-plugin' host: HOST basePath: /s tags: - name: Users schemes: - http paths: /users: post: tags: - Users summary: 创建用户 description: 根据User对象创建用户 operationId: postUser parameters: - in: body name: body required: false schema: $ref: '#/definitions/User' responses: '200': description: successful operation schema: type: string '/users/{id}': get: tags: - Users summary: 获取用户详细信息 description: 根据url的id来获取用户详细信息 operationId: getUser parameters: - name: 'id' in: path required: true type: integer format: int64 responses: '200': description: successful operation schema: $ref: '#/definitions/User' definitions: User: type: object properties: id: type: integer format: int64 name: type: string age: type: string