面向工程师的设计指南 - web api 的请求与响应


恭喜。阅读到本章节的内容代表你领会并且理解了第一章的内容。现在你设计的 Web Api 已经超过了50%的程序员了。从本章节开始我们来完善 api 设计的各方面细节,向 api 设计专家靠拢。

查询的设计

依据上一章节的内容,REST LEVEL2 的查询只需要使用 GET 方法与 URI 定位到相应资源就可以了。但实际业务中查询往往涉及更复杂的细节处理,查询参数就是最富有代表性的细节处理之一。

查询参数

我们所说的查询参数是指:在 http method 为 GET 的查询 api 中,使用 urlencode 的传参方式,既在 URI 的末尾?后面添加的相应参数。

示例
api.linkedin.com/v1/people-search?first-name=Clair

如上所述,first-name 既为你的查询参数对应的 key 值。通常我们会根据不同的业务信息,使用对应的 key 名称。

不过,偶尔你也会看到下面这样的查询参数。

示例
api.instagram.com/v1/users/search?q=jack
api.instagram.com/v1/users/search?name=jack

q 是 query 的缩写。为什么这里不使用可以反映实际业务信息的 key 名称?因为 q 往往表示范围搜索。上述第一个示例表示搜索包含 jack 词组的所有用户列表。而第二个示例表示名称为 jack 的所有用户列表。 所以,后端开发人员需要在 api 的设计中,根据实际情况慎重选择是否要使用 q 或者 query 这个单词作为你的查询参数的 key。 最后看看微软是如何设计搜索引擎的查询参数的。

示例
https://www.bing.com/search?q=URI+test

当 uri 中存在无法直接使用的字符时,会用一种百分号编码的方式对字符进行处理。例如%E3%81等。你可能会认为使用 ASCII 字符集就可以高枕无忧了。但是应该各位注意,%、&、+等字符也需要百分号编码。而空格符号在百分号编码中会被编码为+号。

分页查询

相对位置分页

分页查询的要点在于如何设计分页参数。通常来说基于和数据库类似的 limit 与 offset 的组合相对更通俗易懂。

limit=50&offset=100

但是由于查询往往会涉及到数据库的读操作,而在数据量很大的情况下基于 limit 与 offset 的相对位置的查询组合的缺陷会逐渐暴露出来。试想下面这样的查询参数。

limit=50000&offset=10000

这十分可怕,类似于这样的查询请求对服务器和客户端都是灾难。

绝对位置分页

有一种绝对位置分页的方法可作为你的备选项。它没有使用“从头开始第几条”的描述方法,而是指定了某个 ID 之前,或者日期之前的条件。这样我们查询数据库时可以使用范围匹配来查询出数据返回到客户端,避免了使用数据库的分页功能从而造成性能隐患。

示例
sample.api.com/v1/user/register-before/2017-08-15T00:00:00

客户端请求查询17年8月15日之前注册的所有用户的信息列表。服务器端对于这样的请求使用

select * from user where time < 2017-08-15T00:00:00

即可轻松解决问题。从而避免了从1开始计数的扫表操作。

查询参数与 URI 如何取舍

下面这两个示例,你觉得哪个更好?嘘,先别说话。用心去感受。

示例
A sample.api.com/v1/user/1198723
B sample.api.com/v1/user?userid=1198723

无论你选择了 A 或者 B,希望你都是出自于理性思考而非感觉。 A 与 B 的区别在于 A 的设计将查询参数放到了 URI,而 B 的设计保持了查询参数的原本位置。可能在目前市面上 B 的设计更为常见一点。

类似于这样的查询参数与 URI 取舍的问题,我们最好采用下面这2个决策依据帮助我们进行判断。

  • 参数是否表示唯一的资源所需的信息
  • 是否可以省略

结合参数若能够定位到互联网上唯一的资源,则应该将查询参数放到 URI中。这是基于 URI 的根本含义:统一资源定位符含义来进行设计的。而若查询参数无法配合 URI 定位到唯一的资源甚至与资源无关的话,则应该放到参数位置。 所以,A 的设计在学术理论上更优于 B。这代表着从今天开始,你对于查询接口的设计水平已经超越了贵公司大部分工程师。

响应的设计

相对查询来说响应的设计更考究工程师的细节功底。

性别的设计

性别有两大主流设计。

  1. 使用数值来标识对应的性别。
  2. 使用 male、female。

实际上无论使用哪种都是可以接受的。但是!你需要知道14年 facebook 上线的新系统里性别的可选项目增加到了50种以上。假如你负责设计的接口是供一个 SNS 系统而使用的话,则需要考虑保证社交系统中关于性别的多元化的可能性。所以使用2,比1的可扩展性更强一些。

日期的格式

在 Http 协议的定义中,http header 中通常会使用 UTC 来标识 Http 的时间。因此在设计 api 时,也推荐使用时区“+00:00”。相对于 epochtime 我更喜欢 RFC 3339的日期格式,他更易读并且能够体现出时区的概念。而若要使用 UTC 时间的话,则在后面加上 Z 标识即可。

2017-11-11T13:00:12+00:00
2017-11-11T13:00:12Z
2017-11-11T10:00:00-00:00

-00:00 时区标识时区不明。虽然这种表示很少见,但是正因为少见所以需要重点掌握。

整数

计算机中 int 占用4个字节。如果使用 unsigned 无符号类型的话,则能表示0到4294967295.如果你设计的是一个 SNS 网站,一定要避免用 int 作为返回值,来标识用户的 id 或者其他信息,因为这点空间是完全不够的。将信息量较大的数据统统使用 string 来表示吧。

别名的设计

试想,某日你接到了一个用户模块的设计,其中包括了一个功能:即登录用户可以通过 api 查询用户的个人详细信息。大部分工程师针对此需求很容易设计出以下的代码。

示例
https://sample.api.com/v1/user/info/1987203

response

{
    "userId": "",
    "userName": "",
    "userPassword": ""
}

回想一下你是不是设计过这样的端点?

很可惜,你的代码实际上引发了一个巨大的安全隐患。用户可以传入任何用户的 id 信息查询到对应用户的隐私数据,造成用户信息泄露。

针对这样的需求,在设计此类 api 时应该使用别名来代替用户 id 的传入,防止混淆使用一个接口完成查询其他用户与自身详情两个功能导致信息泄露的问题。

示例
https://sample.api.com/v1/user/info/me

像上述示例中的 api,客户端访问以 me 或者 self 单词结尾的端点,服务器获取到请求后,根据用户 access zoken 查询用户的详细信息返回给客户端使用。

而针对其他用户的信息获取我们采用

示例
https://sample.api.com/v1/user/info/1987203

不同的端点去解决需求。通过使用这样的设计方式,让这两个接口的开发被拆分开来,从而最大限度的降低了混淆使用获取其他用户信息 api 与获取自身用户信息 api 接口的可能性。更容易防止误将其他用户的个人信息对外公开的 bug 发生。

最后

优秀的 Web Api 设计需要大量细节上的考量,是体现一个工程师水平最直观的方式之一。不用怀疑,在学习了本系列文章过后,你就基本掌握了 web api 设计的大部分窍门儿了。 如果你能参照本系列文章的知识点坚持认真对待每一个 api 的设计,那成长成为真正的专家级设计师则指日可待。


Copyright 2017/08/15 by Chuck Lin

参考书籍 《Web Api 的设计与开发》

若文章有幸帮到了您,您可以捐助我。以鼓励我写出更棒的作品!

alipay.jpg-17.7kBwechat.jpg-16.7kB

Copyright © chuck Lin 2017 all right reserved,powered by Gitbook该文件修订时间: 2018-07-15 10:39:59

results matching ""

    No results matching ""