OpenAPI:为传统机器构建智能API

2019 年 9 月 20 日

OpenAPI:为传统机器构建智能API

本文要点

对于开发人员,诸如OpenAPI等API描述规范至关重要。这些规范使得计算机尽可能地承担起API的管理、文档化和执行的重担,解放了开发人员。

OpenAPI 3.0更具表示力,使计算机能担负起更多对开发人员有用的工作。

OpenAPI为测试自动化提供了强大支持。它可用于生成实际可用的模拟测试(Mock),甚至能生成可抽象表示任何开发人员复杂工作的原生绑定。

OpenAPI提供的一些功能并未得到充分利用,例如链接(link)和回调(callback)。这些功能支持直接操作代码,可将开发人员从文档工作中解脱出来。


我们正处于一个 API 时代。即便是对于那些至今依然存活的的非技术企业,也视 API 为其重要产品。越来越多的公司使用 API​​组织文档,作为各团队间分享工作和沟通的基本单元。不少企业试图效仿亚马逊公司的成功做法,不断改进内部和外部的 API。如果《毕业生》电影在 2020 年重拍,那么对于通过数字技术重建年轻的达斯汀-霍夫曼的设想,建议考虑一下“API”。我希望 OpenAPI 提供了我们喜欢的巧克力花生酱。但不幸的是,我能找到对 OpenAPI 最好的概要描述如下:“(OpenAPI)是一种机器可读接口文件的规范”。在如此冰冷的表述下,潜藏着不少非常有用和实用的技术。是的,尽管开发人员能使用机器可用的方式描述自己的 API,但对于构建 API 的团队和使用 API 的软件开发人员而言,无疑更希望机器能完成此类工作。


热切的学习者


当我还是个孩子时,API 参考书目都是此大部头。开发人员必须吃透诸如《某某开发者指南》、《Palm 编程》、《Java 3D API 规范》等书籍。Tim O’Reilly 等出版社毫不留情地收走了我们的购书款。开发人员学习 API 的方式,就是去阅读这些大部头 API 书籍。他们不仅需要具备所使用系统或平台的基本理念,而且还要掌握这些系统或平台的细节工作机制,也就是那些“API 参考书目”。我们意识到,鉴于这些资料已经在因特网上广泛传播,即便是对于最热切的学习者,掌握 API 也需要进行大量的教育培训。培训用户掌握我们所构建的 API,是与构建这些 API 同等重要的工作。


“API”这一术语涵盖多个方面。因此本文略加精炼,将聚焦于 HTTP API,称为 REST。和许多人的错误认识一样,事实上 REST 的范畴略为狭隘,它只是 Web API 的一个子集。读者在 SDK 中会看到很多 API,本文并非无视其它这些 API 的重要性,而是考虑到 Web API 正以前所未有的速度激增。我们私有网络中的 API,看起来越来越类似于开放互联网云服务所用的 API。我也并非要介绍类似于 WSDL 这样的老古董,亦或是 GraphQL 等热点新技术(尽管本文会在稍后提及)。本文意在阐述那些几乎所有 SaaS 供应商都会发布的常用 API。


许多开发人员需要的,并非是要生成那些事实存在于代码中的接口 API,而是需要生成可提供给文档、注册、代码生成等的编程描述信息。OpenAPI 并非描述 API 的唯一规范,但正日益占据统治地位。OpenAPI 最初称为“Swagger”,在捐献给 OpenAPI Initiative 后,重命名为“OpenAPI”(感谢 SmartBear!)。此外,RAML 和 API Blueprint 具有各自的拥趸,虽然数量似乎在下降。AWS、Google 和 Palantir 等企业使用的是自己的 API 规范,因为它们自己的规范是领先于其他标准的,并具有不同的要求,甚至发现 OpenAPI 这样自成体系(opinionate)的规范并不够充分。考虑到 OpenAPI 的日益普及已经催生了大量的工具(包括我们企业自己的SQL layer for APIs),本文主要关注 OpenAPI。


本教程开篇,是介绍如何基于 OpenAPI 描述 API 的行为。当然,这一步的关键输出是可供人阅读的文档,但 OpenAPI 还可指导机器如何使用我们的 API,进一步简化人工工作,实现自动操作。随着我们向 OpenAPI 提供越来越多的信息,开发人员的重担逐渐转移到他们所使用的机器和工具上。鉴于 API 的数量之多,同时软件开发人员也将十分了解这些 API,我们能逐渐从 API 培训工作中解脱出来。API 成为一种重在降低开发人员间摩擦的产品。


OpenAPI 的基础知识


OpenAPI 文件可以使用 JSON 或 YML 定义。下面给出的是摘自Strava OpenAPI文档的部分内容:


  "paths": {    "/athletes/{id}/stats": {      "get": {        "operationId": "getStats",        "summary": "Get Athlete Stats",        "description": "Returns the activity stats of an athlete.",        "parameters": [          {            "name": "id",            "in": "path",            "description": "The identifier of the athlete. Must match the authenticated athlete.",            "required": true,            "type": "integer"          },          {            "$ref": "#/parameters/page"          },          {            "$ref": "#/parameters/perPage"          }        ],        "tags": [          "Athletes"        ],
复制代码


开发人员可以使用工具生成上述文档,也可自己手工撰写文档,或是从多种语言代码中生成文档。下面给出的是 Java 语言的一个例子。该例子实现了在 JAX-RS 标注中添加 OpenAPI 标注:


@GET    @Path("/{username}")    @Operation(summary = "Get user by user name",            responses = {                    @ApiResponse(description = "The user",                            content = @Content(mediaType = "application/json",                                    schema = @Schema(implementation = User.class))),                    @ApiResponse(responseCode = "400", description = "User not found")})    public Response getUserByName(            @Parameter(description = "The name that needs to be fetched. Use user1 for testing. ", required = true) @PathParam("username") String username)            throws ApiException {        User user = userData.findUserByName(username);        if (null != user) {            return Response.ok().entity(user).build();        } else {            throw new NotFoundException(404, "User not found");        }    }
复制代码


OpenAPI 的显式输出物是文档。采用这样的智能工作流,其主要优点是可使文档保持最新。过时的文档无疑令人尴尬和愤怒,OpenAPI 可使文档更为漂亮。开发人员可添加交互式资源管理器等有用组件,或是添加自动生成更改日志,而不仅是描述 API 的输入和输出。


为进一步降低人工工作,OpenAPI 可以基于用户描述的内容,驱动 Mock 服务器发布 API。所发布的 Mock API 会根据规范中的模式和编码,在规范中的特定示例给出响应。这样,在 API 完全构建之前,用户的内部团队即可着手开始工作。外部开发人员甚至无需获得授权访问,也不会在使用程序化测试 API 中向服务器发送大量的垃圾。


生成本机代码绑定,这是我们企业最初使用 OpenAPI 的一个方式。我们使用 OpenAPI 为前端生成 TypeScript 绑定,并与后端进行交互。这样,API 的学习过程从代码和文档中移动到了 IDE 中。开发人员使用 IDE 编辑器,就能给出的各种 API 的接口及正确类型,无需查看服务器端代码才能理清各 API 的工作方式。发布符合 OpenAPI 规范的 API,实现支持开发人员通过浏览代码(即便使用 Vim)了解 API。


OpenAPI 3.0


早在一年多前,OpenAPI Initiative 就发布了 OpenAPI 3.0。但其中一些非常酷的功能,至今依然尚未得到充分利用。需要指出的是,这些功能扩展了描述 API 的能力。Swagger 最初意在构建一个自成体系的子集,用于指定应该要做的事情,而不是涵盖并参数化特定 API 的方方面面。我很高兴看到,这一理念在 OpenAPI 的后续版本中得以延续。3.0 版引入了两个非常酷的元特性,即链接(link)和回调(callback)。


链接:通常情况下,一个 API 的调用结果,可以作为另一个 API 调用的输入。开发人员可能会在 API 的响应体中看到包含有文本链接。OpenAPI 的链接功能,添加了描述不同 API 间链接的静态元数据,定义如何使用一个 API 的输出作为另一个 API 的输入。GraphQL 中也正在添加此特性,即以图中边的形式明确编码实体间的链接。链接特性似乎并未得到 API 规范编写人员和一些工具软件的充分利用。现在还具有很多其它的做法,例如超链接文档、交互式 API 资源管理器和自动 GraphQL 转换层等。很高兴看到越来越多的 OpenAPI 文档使用链接或是更好的工具去指定链接。


回调:在注册 WebHook 时,通常会将 URL 以字符串方式传入,然后服务将调用该 API。OpenAPI 3.0 支持用户去描述回调的签名,即回调所应具有的参数。同样,回调非常有助于开发人员摆脱文档,通过浏览代码了解 API。这正是开发人员希望的首选做法!


更多介绍


API 创建者使用 OpenAPI,可有效降低培训 API 使用者的负担,而更甚者,OpenAPI 可使开发人员只需了解更少的内容就能更好地掌握 API。开发人员也会注意到,API 创建者能将培训聚焦于 API 的关键信息,而非那些可机器自动化完成的逻辑细节。OpenAPI 还提供了更多的功能,支持开发人员所使用的机器去理解 API,而非让开发人员亲历亲为去理解。以“分页”(pagination)功能为例,下面给出 Google Calendar API 向使用者阐释日历事件分页的方法:


输入:


pageToken 字符串 用于指定结果页面返回的 Token(可选)。


输出:


nextPageToken 字符串 用于访问结果下一个页面的 Token。如果没有进一步可用的结果,可忽视,此时给出 nextSyncToken。


如果仔细阅读文档,可看出在每次连续调用中,开发人员应该从 nextPageToken 获取输出,并将输出插入到的 pageToken 输入中。但是 OpenAPI 中并未提供表达上述语义的方法。同样,Google专有的Discovery文档格式中也并未提供。


另一种 OpenAPI 扩展其表示力的常见模式,针对的是可能需要数秒甚至更长时间才能完成的操作。例如,在公有云中创建虚拟机实例时,需要一次调用完成启动创建实例的过程,另一次调用确定实例何时启动并运行。AWS 的 EC2 RunInstances 文档对此给出如下阐述:


实例一旦处于运行状态,即可使用。用户可使用DescribeInstances检查实例的状态。


如果在 API 规范中编码了上述语义,并且开发人员所使用的工具可以抽象化此类语义,那么无疑可极大地提升开发人员的生活质量。为机器提供更多可理解的细节,可更大地降低开发人员的学习成本。


总结


对于已构建的 API,或是正在构建的新 API,请使用 API 描述规范。OpenAPI 是一种日益广为使用的规范。即便该规范并不适用于你,但你仍可从中去芜存菁(但请告知 OpenAPI 团队不适用的原因)。越大程度上遵循规范,越能发现使用工具生态系统所带来的好处。


那么应如何着手去使用 OpenAPI(或类似的规范)呢?描述 API 的过程颇具争议。规范的坚定支持者打着“契约优先”(contract-first)的旗号做事,他们在 API 项目一开始就使用 API 规范,进而生成 Stub 代码、客户端等。对于已有的 API,更程序化的做法是使用一些常用的 Web 框架工具,从代码中提取规范。在一些情况下,需要辅以代码注释或其它一些辅助信息。


无论是“契约优先”还是“代码优先”(code-first),实际上取决于开发人员自己的工作流程。对于一些大型组织而言,契约优先会在同一处明确指定了服务端和客户端团队的正确获取方法。在编写服务端代码时,客户端团队可以针对自动生成的 Mock 编写代码。对于那些客户端和服务端共同开发的工作组,或是那些以 API 为产品的工作组,适用代码优先的方式。类似地,对于已有 API,契约优先是交付的首选方式。在这些情况下,开发人员可以使用一些常用的 Web 框架生成 OpenAPI 文档(当然,在一些情况下仍需要辅以代码注释或其它辅助信息)。


现在,读者应该已经了解了 API 规范。那么,读者应做的最重要的事情,就是使用 API 规范发布 API。发布规范的 API,并使其保持最新。针对内部使用充分利用这个规范,即生成服务器端 Stub 和客户端代码、构建自动 Mock、生成可供开发人员使用的 API 文档等。通过发布 API 文档本身,开发人员就可以指导机器操作 API。使用规范生成的 Stub、Mock 和测试,意味着开发人员可以花更少的时间去了解 API 的底层细节,用更多的时间去做建设。随着 OpenAPI 及其工具生态系统的持续发展,随着生态系统所使用的库、框架和平台变得更加智能,API 使用者的学习成本将不断降低。


作者简介


Adam Leventhal 是Transposit的创始人,并任 CEO。在 2016 年创立 Transposit 之前,Adam 曾负责开发、质保、支持、程序管理、产品管理、校园招聘和高级管理团队,实现了开发团队从 10 人规模扩展到百人规模。Adam 曾在 Sun Microsystems 的 Solaris 内核开发组(Kernel Development)任主任工程师(Staff Engineer),是 DTrace 的三位作者之一,并在 2004 年因技术贡献卓越荣获 Sun 的主席奖(Chairman’s Award),在 2005 年获 InfoWorld 的创新人士奖(Innovators),在 2006 年荣获华尔街日报的“创新奖”最高荣誉。Adam 已开发了多种项目调试和事后分析(post-mortem)工具,一直致力于扩展 DTrace 系统视图广度和深度的用户操作追踪工作。Adam 于 2001 年以优异成绩毕业于布朗大学,获得数学和计算机科学学位。


原文链接:


Using OpenAPI to Build Smart APIs for Dumb Machines


2019 年 9 月 20 日 08:00870
用户头像

发布了 364 篇内容, 共 88.1 次阅读, 收获喜欢 160 次。

关注

评论

发布
暂无评论
发现更多内容

HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第5章CSS盒子模型

Geek_8dbdc1

CSS

一个文学青年的至暗时刻

半亩房顶

反思 就业

哪些资源容易造成性能瓶颈

彭阿三

Week08总结

熊威

作业-第八周

superman

EasyDL全新升级,文心(ERNIE)3项能力助力快速定制企业级NLP模型

百度大脑

人工智能 nlp 百度大脑

架构师训练营——第8周学习总结

jiangnanage

从 1.9 到 1.11,聊聊 PyFlink 的核心功能演进(附 Demo 代码)

Apache Flink

flink

HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第4章CSS文本样式

Geek_8dbdc1

CSS

Week8作业

丿淡忘

数据结构&网络通讯原理

石印掌纹

判断两单链表是否相交

石印掌纹

week8 作业

a晖

第八周课程总结

考尔菲德

第八周作业

李白

数据结构与算法、网络模型总结

2流程序员

架构师训练营week8作业

小高

第八周作业

Geek_a327d3

架构师训练营——第8周作业

jiangnanage

HTML5+CSS3前端入门教程---从0开始通过一个商城实例手把手教你学习PC端和移动端页面开发第3章初识CSS

Geek_8dbdc1

CSS

IntelliJ IDEA 2020.2正式发布,诸多亮点总有几款能助你提效

YourBatman

IDEA 新特性 IntelliJ IDEA

架构师训练营Week8作业总结

小高

第八周总结

李白

找出两个链表交点(golang版)

2流程序员

Week08作业

熊威

领域驱动设计 学习笔记

半亩房顶

DDD

初识 - DDD-CQRS

半亩房顶

DDD CQRS

第八周心得

方堃

week 08作业

Safufu

架构师训练营作业

邵帅

作业:链表交叉点

考尔菲德

OpenAPI:为传统机器构建智能API-InfoQ