现代社会是个契约社会,日子中大大小小的工作都在和契约打交道。去奥莱买件衣服,一纸小票,便是你跟商家的契约:你花钱买到了产品,产品的问题商家会许诺处理(退换货)。假如你用信用卡交款,你和银行之间,银行和商家之间又达成了一系列契约:银行会在未来的某个时刻扣除你的 credit,这 credit 你需求用钱来换回;银行一起欠下商户简直等值的 credit,这 credit 会在月末交给商户。
契约
契约在软件上最根本的表现便是函数。当一个函数被界说出来时:它告知它的运用者,你我之间应该怎么协作。
比如说,一个函数可所以这样界说的:假如你传递给我类型为 X 的数据,我会回来给你类型为 Y 的成果,并且假如你传递相同的值进来,我给你相同的成果。这是 pure function,也是程序员最喜欢的契约方法,由于黑纸白字,清清楚楚,童叟无欺。
更遍及的状况是不那么朴实的函数:假如你传递给我类型为 X 的数据,我会回来给你类型为 Y 的成果,当然,假如成果不存在,我会给你个 null,爱谁谁;并且,假如我中心处理的进程出了差池,我会扔一颗或许若干颗叫「反常」的炸弹,咱俩要么哥俩好(你处理反常),要么玉石俱焚(不处理)。此外,我不能确保你传递相同的值进来,都回来给你相同的成果(比如说数据库操作)。有副作用的函数虽然有许多含混不清的当地,任然不失为一种契约。
函数等级的契约的一切当事人都是程序员,契约更新的影响面有限,所以遇到问题,姐弟俩一商议,改!新的契约就呈现了。但是,新的契约呈现并不意味着旧的契约的停止,只有当一切运用旧契约的当地都改用新契约时,咱们才干安全地废弃旧契约。就一个函数来说,假如是两人之间的事,替换契约也便是个把小时的工作;但是,像 linux 这样杂乱的体系,你改一个 list_add_tail()
的接口试试(假定你有权限),即使 Linus 不拍死你,我确保社区的口水也要淹死你。为啥?你触动了许多人的奶酪。
铺垫了这么多,便是想阐明一件事:一旦你拟定了一纸契约,你有必要恪守它,且不要简略改动它;运用契约的人越多,改动的价值越大。咱们界说一个陈氏指数 CEI —— 契约运用指数(Contract Employ Index),每百万运用者记为 1。CEI 越高,标明运用者越多,相同的,改动的价值就越大。
REST API(以下凡说到 API,都指 REST API)是什么?REST API 是服务器和客户端之间的契约。这就意味着一个中小规划的 API,其 CEI 最少在 0.1 以上。API 一旦发布,你根本失去了对其恣意修正的权力,由于你无法等待脱离了掌控的客户端能够像咱们希望的那样,步骤共同地晋级体系。
所以,即使你习惯于为所欲为地创立一个函数,然后在需求的时分重构之,做 API 时,你会遭到许多掣肘。老子说:「夫轻诺必寡信,多易必多难」,你一开端随意了,简略了,会给之后的保护和更新带来无穷无尽的苦楚。
所以咱们需求好好进行规划 API 的接口。
界说和规划契约
咱们知道,规划接口并不是一件轻松的活,咱们要考虑:userability,simplicity,security,reliability 等等,规划好了还需求将其文档化。所以咱们最好借助于东西的力气来规划 API,就像咱们运用 visio 规划网络拓扑或许软件架构相同。现在比较盛行的 API 接口规划东西有 swagger,API blueprint 和 RAML。
它们一起的特点是你能够很方便地描绘 API 的输入输出,并生成交互式的 API 文档。所谓交互式 API 文档,是指用户在读 API 文档的时分,能够在线运转 API,取得成果。这样,API 的规划者就能够在还没有开端写代码的时分就重复推演 API 的结构,直到发生一个强健的,明晰明晰,可用性强的接口。
Swagger
swagger 是最早也是最老练的 API 接口规划东西。它能够运用 json/yaml 来描绘 API 的接口,运用 swagger 来规划和描绘 API 有许多优点:API 的文档化,API 的接口的可视化,各种言语的客户端类库的主动生成,乃至服务端代码也能够主动生成。包含代码生成东西在内的完好而老练的东西链是 swagger 的杀手锏,也是许多 API 厂商优先挑选 swagger 的一个重要因素。
咱们看 swagger 的一个比如(instagram API):
这儿界说了一个 API endpint /users/self/feed
,他承受三个 querystring 参数,并在恳求成功时(200)回来一个这样的目标:
{ "data": [...]
}
swagger 的缺陷是太冗杂,编撰起来很费事。不过,这不是什么大问题,所以我终究挑选了 swagger。
API Blueprint
API Blueprint 更倾向 API 的文档化,所以它挑选的描绘言语是 markdown。三者之间 API blueprint 的描绘言语可读性最强,更像是真的在编撰文档。
但是,markdown 的强项不在表述语法,validation 相关的内容用 markdown 描绘不是很舒畅,看他人写的文档很简略理解,自己写起来就会讹夺百出。API blueprint 的东西链也是个薄弱环节,许多东西都没有或许不老练。假如说东西的缺少还能够经过时刻来补偿,运用 markdown 这种对机器不太友爱的界说言语来界说各种语法,则是 API blueprint 犯下的大错。由于,比照三者的语法,它们的学习曲线都很长,忘记指数都很高(不是经常用),盼望程序员来写还不如盼望机器帮你生成。而机器生成强语法结构的 json / yaml 相对简略,生成弱语法结构的 markdown 则要填不少坑。
所以,权衡之下,三者之间,我最早筛选的是 API blueprint。
RAML
RAML 运用 yaml 来描绘 API。它被规划地很灵敏,很简略把描绘分化到多个文件里然后彼此引证。
就描绘言语来说,RAML 像是一个繁荣向上的少年,精明而干练;而 swagger 现已垂垂老矣,冗长而庸俗。我一开端在 RAML 和 swagger 两者间左右摇摆,写了不少测验代码,假如不是 swagger 的东西链过于吸引人,而 RAML 1.0 版别还处在 beta 阶段,我或许会终究挑选 RAML。
契约和完结合二为一
假如咱们从 swagger 动身,规划好 API 的接口,然后再用某种言语完结这个接口,显得有些负担,日后改接口时,得改代码;改代码后假如变动了接口,还得回头改 swagger 的声明,这样太累心,早晚会出不共同的问题。一旦不共同,之前所做的一切尽力就落空了:你供给了契约,却没有依照契约去行事。
swagger 考虑到了这一点,它能帮你生成客户端的 SDK 和服务器端的 stub。客户端的 SDK 还好,客户端的其他代码都是单向调用 SDK,从头生成并不会影响太大;服务端的代码需求 API 完结者完结,即使生成了 stub,肯定是要修正和添加功用的,所以假如修正 swagger 文档后,你不能再从头生成服务器端的 stub 了,由于这样有或许覆盖掉你现已修正的代码。所以咱们运用 swagger 的方法根本都是服务器这端彻底自己写,不用 stub。这样的话,上述的问题仍旧存在。
另一种解决方案是经过 API 代码反向生成 swagger 文档。乍一看这好像违反了 API 描绘言语的初衷:咱们居然在没开端规划之前,就开端写代码了。
不用过虑。咱们能够把代码的结构调整地更靠近描绘言语。你能够先编撰代码把 API 的输入输出界说清楚,然后经过这个界说来生成 swagger 文档,在 swagger-ui 里边调试和验证;当托言规划契合希望后,再完结详细的完结。比如说这样用代码描绘 API:
抛开 action 是什么不提,这段代码简直和你用 YAML 描绘 API 的接口千篇一律(这儿缺了描绘 response 的内容)。咱们能够运用它生成 swagger 文档来验证:
经过代码反向生成 swagger 文档的优点是代码和文档总是共同的,API 的完结和契约彼此印证;缺陷是程序员看见代码就像看见九天仙女相同,眼迷心荡,刚界说好接口,还未细思,就忙不迭地去完结了。
结尾
这个系列居然写出了五篇文章,大大出乎我的预料。韩非子说:善张网者,引其纲。不逐个摄万目然后得。做 API 是个提纲携领的活,你要从纷扰的「万目」中找到那根系网的大绳,触动之,网就搭好了。这根绳,不消说,便是我在 再谈 API 的编撰 - 架构 那篇文章中所述的 Pipeline。以此为纲,自顶向下,层层递进,你便恍然大悟。但是,API 写得再好,没有一个与之对应的契约,是万万不可的 —— 没有文档描绘的 API 就好像没有阐明书的产品。文档和代码,好像泉流干枯之后相呴以湿,相濡以沫的鱼儿,谁也离不开谁。能否避免它们跟着时刻的消逝功用的添加而相忘于江湖,是考量每个程序员才能和操行的一杆秤。