谈 API 的撰写 - 子系统
在做一个系统时,有一些子系统几乎是必备的:配置管理,CLI,以及测试框架。
配置管理
我们先说配置管理。一个系统的灵活度,和它的配置管理是离不开的。系统中存在的大量的预置的属性(下文简称 property),需要有一个公共的地方来放置。这里我不说「常量」,而是说「预置的属性」,是因为这属性可能需要在运行时发生改变,而常量的范畴会让人有所误解。
最简单的配置管理就是把所有的 property 放在一个配置文件中,在系统启动的时候读入。配置文件的类型有很多选择:ini,json,yaml,toml 等。这些类型各有优劣,选择的时候注意配置文件最好能够支持注释,便于维护。从这个角度看,json 不是个太好的选择。toml 可能大家用得不多,它是 github 创始人 Tom 定义的一种格式,类似于 ini 但灵活不少,感兴趣的可以在 github 里搜索 toml。
我们知道,一个项目会有多种运行时:development,staging,production,test。不同的运行时加载的配置文件可能不同。所以配置管理需要考虑这一点,让配置文件可以重载(override)。最常见的重载策略是系统提供一个公共的配置文件:default,然后各种运行时相关的配置文件继承并局部重载这个配置。在系统启动的时候,二者合并。
有些时候,我们需要在系统运行的时候改写配置。由于配置一般在系统初始化的时候就被读入内存,所以单纯改写配置文件无法即时生效,这时,你需要像管理缓存一样去管理和配置相关的数据,将其封装在一个容器里:当配置被修改时,调用这个容器的 invalidate 方法 —— 这样,下次访问任意一个配置项时,会重新读入配置,并缓存起来。
对于分布式的项目,配置应该集中存储在诸如 redis 这样的系统,以方便统一处理(orchestrate)。
CLI
写 CLI 并非难事,但一个 CLI 子系统的难点是:
CLI 的发现和自注册。你的 framework 的用户只要遵循某种 convention 撰写 CLI,这些 CLI 就会被自动集成到系统里。
CLI 的撰写者能够轻松地获取到系统的信息,也就是说,系统有自省(introspection)的能力。
前者的实现我们在前面的篇章里(谈谈编译和运行)讲路由是如何注册的已经提到,这里就不赘述。后者非常重要,在展开讨论之前,我们先考虑一个问题:做一个系统的过程中,我们希望这个系统的 CLI 解决什么问题?
首先,CLI 显然不是给用户用的,是给程序员用的,所以,CLI 提供一些简化程序员工作的脚本。那么,作为一个 API 系统,程序员都需要哪些 CLI 呢?我们看一些例子:
创建某些 skeleton - rails / django 都有新建项目,新建 model / controller 等的 CLI
获取系统的信息。比如:不用看代码就能很快知道系统里都有哪些 route,哪些 model 等。
生成某些信息或者模拟某些行为。当你调试你的系统时,每次生成某种状态很烦人,比如说登录,可以通过 CLI 一键完成。
...
这些例子大部分都需要系统的自省的能力,比如说下面这个 CLI:
在这个 CLI 的执行函数里,我们使用了这些系统信息:
系统中所有 app 的名字。
某个 app 的 instance。
app 的配置信息。
app 启用的 middleware。
app 的所有的路由。
...
如果我们无法在系统的非运行时获取这些信息,那么,CLI 的威力会大打折扣。这也印证了我之前的文章 里所述的将「编译时」和「运行时」分开的重要性。很多框架,如 express.js,由于无法很清晰地将二者区分开,以至于你想在非运行的时刻获取 route / middleware 的信息,非常困难。
测试框架
API 的测试是相当无趣的(几乎所有的测试例撰写起来都相当无趣),但是测试的重要性是不容置疑的,尤其对于一个不断重构的代码。如果说别的系统的测试只能在局部寻找规律而进行优化,API 的测试,尤其是 functional testing 是可以全局考虑的。
比如你有一个 API 是 PUT /feature/:id
,要测试这个 API 是否工作正常,你大概会考虑这些测试例:
PUT 正确的数据到一个错误的 id,测试是否会出错;
PUT 错误的 etag,测试 concurrent udpate 是否工作;
PUT 空数据,测试 validator 是否正常工作;
PUT 错误的数据,测试 validator 是否正常工作;
PUT 正常的数据,测试基本功能是否工作。
这些测试例有这些共同之处:
需要运行一个 temporary server
需要发送请求到 temporary server 上
需要检测 status code,以及 response header / body 来确认是否出现期待的结果
如果每个测试都写一个测试例,虽然每个的代码量并不太大,但测试一个 API 就需要 5 个测试,API 的规模一上,代码量就大了,添加和维护都很麻烦。
我们可以定义一种针对于此的测试语言来描述测试的 fixture:
这个定义非常简单,相信大家都能看明白:
测试的描述(用于 test report)
测试所用的 url
期待的结果,包括 status code,headers 和 body
这里面,我们用了一种很简单的方式区分 field name 和函数。比如 body 下面的 #length
,它的结果不是body['length']
而是 length(body)
,前者虽然对 array 有效(javascript),但对 object 无效。
要运行这样的 fixture,并不需要撰写太多的代码(假设我们是用 ava 作为测试工具):
这里面,runAssertion
发送 request,并对比 fixture 里面的数据和 response,来确定一个 test case pass 或者 fail。
这样下来,我们成功地把繁琐的 test case 的撰写转化成一个 parser 和一系列 fixture 的撰写。parser 的撰写是一次性的,以后改动很少(但会添加新的功能,比如新的函数 - 如上的 #xxx),而 fixture 的撰写对比着之前的例子,几乎很难出错。这样的测试例,你三五分钟写出一个来是轻而易举的事情。
谈 API 的撰写 - 子系统的更多相关文章
- 谈 API 的撰写 - 架构
在 谈 API 的撰写 - 总览 里我们谈到了做一个 API 系统的基本思路和一些组件的选型,今天谈谈架构. 部署 首先要考虑的架构是部署的架构.部署的方案往往会深刻影响着系统的结构.我们需要问自己一 ...
- 谈 API 的撰写 - 总览
背景 之前团队主要的工作就是做一套 REST API.我接手这个工作时发现那些API写的比较业余,没有考虑几个基础的HTTP/1.1 RFC(2616,7232,5988等等)的实现,于是我花了些时间 ...
- 浅谈 Linux 内核无线子系统
浅谈 Linux 内核无线子系统 本文目录 1. 全局概览 2. 模块间接口 3. 数据路径与管理路径 4. 数据包是如何被发送? 5. 谈谈管理路径 6. 数据包又是如何被接收? 7. 总结一下 L ...
- (转)浅谈 Linux 内核无线子系统
前言 Linux 内核是如何实现无线网络接口呢?数据包是通过怎样的方式被发送和接收呢? 刚开始工作接触 Linux 无线网络时,我曾迷失在浩瀚的基础代码中,寻找具有介绍性的材料来回答如上面提到的那些高 ...
- 再谈API GateWay服务网关
前面在谈微服务架构的时候,我博客上转过Chris Richardson 微服务系列中对微服务网关的描述: 通常来说,使用 API 网关是更好的解决方式.API 网关是一个服务器,也可以说是进入系统的唯 ...
- 浅谈API网关(API Gateway)如何承载API经济生态链
序言 API经济生态链已经在全球范围覆盖, 绝大多数企业都已经走在数字化转型的道路上,API成为企业连接业务的核心载体, 并产生巨大的盈利空间.快速增长的API规模以及调用量,使得企业IT在架构上.模 ...
- 浅谈API设计
为什么需要了解一些API设计? 只要你编程,你就是API Designer 一个好的设计,模块之间的耦合应该也是API级别的 一个程序,如果你独立开发,那你既是API的Designer,也是API的U ...
- 浅谈API安全设计
一.简述 安全是恒久的话题,如果不注意防范,会带来很严重的后果.比如: 1.接口被大规模调用消耗系统资源,影响系统的正常访问,甚至系统瘫痪 2.数据泄露 3.伪造(篡改)数据,制造垃圾数据 4.App ...
- 谈API网关的背景、架构以及落地方案
Chris Richardson曾经在他的博客上详细介绍过API网关,包括API网关的背景.解决方案以及案例.对于大多数基于微服务的应用程序而言,API网关都应该是系统的入口,它会负责服务请求路由.组 ...
随机推荐
- python正则 转
python中的正则表达式(re模块) 一.简介 正则表达式本身是一种小型的.高度专业化的编程语言,而在python中,通过内嵌集成re模块,程序媛们可以直接调用来实现正则匹配.正则表达式模式被编 ...
- Win右键管理员权限的获取
Windows Registry Editor Version 5.00 ;取得文件修改权限 [HKEY_CLASSES_ROOT\*\shell\runas] @="管理员权限" ...
- 【bzoj3555】[Ctsc2014]企鹅QQ 字符串hash
题目描述 PenguinQQ是中国最大.最具影响力的SNS(Social Networking Services)网站,以实名制为基础,为用户提供日志.群.即时通讯.相册.集市等丰富强大的互联网功能体 ...
- ZigBee学习三 UART通信
ZigBee学习三 UART通信 在使用串口时,只需掌握ZigBee协议栈提供的串口操作相关的三个函数即可. uint8 HalUARTOpen(uint8 port,halUARTCfg_t *co ...
- centos安装arm交叉工具链后常见的问题解决
[root@localhost osdrv]# arm-hisiv400-linux-gcc -vbash: /opt/hisi-linux/x86-arm/arm-hisiv400-linux/ta ...
- 四则运算出题系统,java
程序设计思想: 首先通过判断选择计算的范围,然后用随机数生成两个随机数,定义另一个数,将两个随机数计算得到的值赋给定义的数 程序代码: package Kaos1; import java.util. ...
- Python 安装MySQLdb模块遇到报错及解决方案:_mysql.c(42) : fatal error C1083: Cannot open include file: 'config-win.h': No such file or directory
一.问题 系统:win7 64位 在下载MySQL-python-1.2.5.zip,使用python setup.py install 安装时,出现以下报错: _mysql.c(42) : fata ...
- P1143 飘飘乎居士的约会
P1143 飘飘乎居士的约会 时间: 1000ms / 空间: 131072KiB / Java类名: Main 背景 一阵狂风吹过 只听“pong”的一声,飘飘乎居士降落了!!! 描述 又是美妙 ...
- POJ3983 快算24
很正常的题目,迷一样的答案. 测试数据只有一组,对没错只有一组. #include<cstdio> int main() { printf("5*(5-(1/5))\n" ...
- Javascript&Ajax-深入浅出JSONP--解决ajax跨域问题
Javascript&Ajax-深入浅出JSONP--解决ajax跨域问题 原理讲解: 链接地址:http://www.cnblogs.com/chopper/archive/2012/03/ ...