《Web API设计浅谈系列二》 API端点的设计

本系列共7小节,内容大部分来自《Web API的设计与开发》的读后笔记,也有部分是来自平时的经历。

本系列源自近段时间在开发、设计会员系统API时遇到的问题、学到的设计方法及自己的一点经验感悟,在此做简单记录。

《Web API设计浅谈系列二》 API端点的设计

端点指的是API的URI,更多的一般我们特指URL比较多,如http://api.example.com/v1/user/me.

1. 端点的基本设计

端点的设计的原则是容易记忆,URI包含的功能一目了然。具体可以参考一下几个原则:

(a) 短小便于输入的URI

尽量做到URI简单但又能表述其功能,避免冗长、重复的内容。如一个表示搜索功能的URI:http://api.example.com/service/api/search,就不如下面的简短且易记忆:http://api.example.com/search

(b) 人可以读懂的URI

API是供别人阅读使用的,所以在URI设计上一定要清晰可读。如URI上使用/u就不如/user来的清晰明确。设计时注意:

  • 不用中文拼音
  • 尽量避免缩写,除非是大家都很熟悉的,如API
  • 名词使用单复数,如users代表返回多个用户,user只表示返回一个用户
  • 用词尽量做到准确,如find/search.这一点上最好的做法就是模仿,比如可参考Github API文档ProgrammableWeb

(c) 没有大小写混用的URI

大小写混用会导致URI难以记忆,一般的为了和域名保持一致性,通常将URI保持全小写。在某些情况下,也有API提供商对大小写混用的URI做了兼容或报错处理,笔者在实际开发中就直接返回404错误了。

(d) 修改方便的URI

修改方便的URI是指某个URI能够非常方便的将其修改成另外一个URI。 如http://api.example.com/v1/items/123表示获取产品ID为123的信息,那么很容易的,我们便能猜测出换一个ID便能得到其他的产品信息。

(e) 不会暴露服务器端架构的URI

作为提供API的服务端,其内部架构信息如编程语言、目录结构、系统类型等信息对于客户端来说都是无关紧要的。因此在使用URI时将这些信息暴露一个是显得多余,另外一个是可能会产生安全问题。如http://api.example.com/v1/get_user.php?id=123,显示出服务器端语言是PHP,一般只需要提现API的功能、数据结构和含义即可。

(f) 规则统一的URI

统一规则的URI能够使API易于使用、理解,如URI的单复数统一,参数名一致等。笔者在最初的开发中就犯过此类错误,如性别字段都是使用字符来表示,由于一开始没有使用枚举处理,导致后面将两个性别代码混淆了。而API和文档却已公开了,给后续开发、修改带了很大的痛苦。

2. HTTP方法与端点

HTTP协议中提供了很多方法用来表示业务的操作类型,在URI的设计中,业务操作使用对应这些方法能易于理解。

  • GET: 获取资源
  • POST: 新增资源
  • PUT: 更新已有资源
  • DELETE: 删除资源
  • PATCH: 更新部分资源
  • HEAD: 获取资源的源信息

而在笔者的实际开发中,借鉴于微信开发的设计,一般只使用到GET和POST两个方法,GET用来从服务器获取数据(对服务器数据无修改),POST用来处理对服务器数据产生变更的操作。

对于URI设计,考虑到RESTful风格,参考下图(出自维基百科RESTful API)

HTTP请求方法在RESTful API中的典型应用.png

3. API端点的设计

(a) 使用名词的复数形式

如user/123表示获取单个用户信息,而users表示多个用户的集合,类似的还有像friends这种具备单复数含义的单词等。

(b) 选用准确的单词

老生常谈的话题,如find/search这些看似相同实际也有区别的词组,英语不好要做到这一点是比较难的。

(c) 不用空格及需要编码的字符

有些特殊字符使用到URL中需要进行编码,如%、中文等,这种情况是需要避免的,因为经编码后的URL无法一眼看出其业务含义,且不利于输入。

(d) 使用连接符来连接多个单词

多个单词的连接方式一般有以下三种:

  • 蛇形法(Snake Case): profile_image
  • 驼峰法(Camel Case): profileImage
  • 脊柱法(Spinal-Case): profile-image

任选一种,上下保持一致即可。 更好的方式是,在URI中尽量避免使用多个字符,而用profile/image形式进行分割处理。

4. 搜索和查询参数的设计

(a) 分页

  • 相对位置

分页中常见的参数组合为page/per_page, limit/offset, 推荐使用后者,因其自由度更高,如表示“获取从第100条开始的20条记录”需求,使用page/per_page可能其page=50时显得并不好拼凑。

不管是page还是offset都是以相对位置来获取记录的,都会存在性能问题。因为相对未知取数需要将大量的记录从数据库或其他存储空间读取到内存中,这必然导致性能问题。

相对位置还可能会引起脏读。对更新很频繁的数据,先读取前20条记录,紧接着继续读取后续的10条记录时,可能在此期间在前面插入了两条记录,那么该次读取前两条记录实际上是前一次的20条中的最后两条。

  • 绝对位置

使用绝对位置可以解决上述问题,利用max_id/since_id, 来制定要读取的最大/最小ID值。有时在一些有datetime字段的表中,也可以使用datetime字段进行处理。

(b) 过滤

过滤参数通常可以放在路径或查询参数上,这个需要看API的整体设计来决定,需要注意的是,一般的我们对一些URI接口都提供一些缺省值,如分页选项的的page可选,默认为50.

5. 登录和OAuth 2.0

一般的使用具体的API时都需要先登录获取access_token, 这是一个身份的认定过程,也是后续业务API执行的前提。基本上除了一些公共的、开放资源的API(如获取天气信息API)不需要登录外,具体的业务系统都是需要进行登录授权的。

登录操作可以使用API设计的本身的用户系统信息,也可以使用OAuth验证,如在一些社交类网站中就经常看到使用微博或微信账号体系进行登录。OAuth(开放授权)是一个开放标准,允许用户让第三方应用访问该用户在某一个网站上存储的私密的资源(如照片、联系人列表),而无需将用户名和密码提供给第三方应用。

登录成功后获取的access_token一般都具有有效期,具体长短可根据具体情况进行设定。因此,在登录接口返回时就必须指明改access_token的失效时间。

{
    "access_token": "f824d4cf-ecf6-4966-ab3c-0db3431d7190",
    "expires_in": 2629743
}

expires_in的值应该是未来某一个具体的时间,其格式可以是Datetime或者是转换成毫秒数,而应避免返回从当前开始时间到失效时的秒数,因为从API返回到客户端接收处理时是有时间差的,可能导致该值的差异。

6. SSKDs与API的设计

SSKD是指API面向内部少数的程序员使用的,其关注点在于具备通用性并易于理解和使用,有时需要把终端用户的用户体验的优先级放到API规则设计之上。之前有人提出“一个屏幕的内容使用一次API调用,一次保存使用使用一次API调用”的观点。笔者对此表示还是得看具体的业务情况才行,并不适用大部分情况。

比如,在会员中心首页需要显示用户即将过期的优惠券信息,从通用性角度来看可能需要先获取会员信息,然后再获取该会员将要过期的优惠券信息。如此需调用两次API,为了减少与服务端的交互,我们在获取用户信息的接口中直接将优惠券信息返回,这样尽量的做到一次交互便能获取到首页信息。

标签: web api, rest, json, http协议

添加新评论