API 设计:让牛懂琴

对于 API 设计,这个演示稿讲得实存是太赞了,等不及的同学马上了解一下《RESTful API Design, Second Edition》的这 107 页吧。如果希望先听我讲讲 10 分钟收获的同学,可以先看下面的描述。


以为需要买本书,看完整本才能明白什么是好的接口。没想到,利用 HTTP Verbs 和 URL,可以这样做非常简洁的 API。“对牛弹琴”被用以描述做无效劳动,可见弹琴者几乎做的都是无用功,而作者以深入浅出的方式让牛听懂琴变成了 — 甚至像我这样的牛 — 在 10 分钟之内立马理解。其主要需要注意的点可以归纳为以下几句:

保持 RESTful。只需 2 种 URL。不使用动词。使用复数名词。具体优于抽象。提供 JSON 的原则是支持 JS 调用。把复杂的参数放于 ? 号后面。借鉴优秀的 API。提供异常描述。提供库或者 SDK。

这里面值得强调,或者说可能需要一点解析的是这几点:

一、只需 2 种 URL

结合 HTTP Verbs,我们通常只需要 2 种 URL,一种用于集合的调用,一种用于单个实例的调用:

  • /dogs :用于调用狗狗列表
  • /dogs/1234 用于调用 dogs 这个列表中 ID 为 1234 的小狗

所有 API 都一样。其实我想你想到的可能是这样的形式,因为毕竟会需要有四种动作:创建 / 查看 / 修改 / 删除

  • /dogs [GET] 查看狗狗列表
  • /dogs/edit [PUT] 批量修改
  • /dogs/delete [DELETE] 批量删除
  • /dogs/1234 [GET] 查看 ID 为 1234 的小狗
  • /dogs/1234/edit [PUT] 对小狗做点什么
  • /dogs/1234/delete [DELETE] 删除小狗的存在

看起来不错。或者还可以变成 /dogCreate 之类的来减少一层,但还是不能实质上减少数目。这时太多接口总是导致记错,并且还需要记得 HTTP Verbs 与 URL 的对应。代码层面上也比较难做(或者应该对比下后面的实现)。何不考虑与 HTTP Verbs 结合:

  • POST –> Create 创新
  • GET –> Read 查看
  • PUT –> Update 修改
  • DELETE –> Delete 删除

这个时候我们只需要 2 种 URL,与 HTTP Verbs 充分结合:

POST GET PUT DELETE
/dogs 创建 查看列表 批量修改 删除所有
/dogs/1234 error 查看单个实例 修改单个实例 删除单个实例

 二、提供 JSON 的原则是支持 JS 调用

这点似乎一点难理解,先让我们来看一下几个设计吧:

// twitter
"created_at": "Thu Nov 03 05:19:38 +0000 2011"

//bing
"DateTime": "2011-10-29T09:35:00Z"

//Foursquare
"createdAt": 1320296464

这里假设我们有两种需要,一种是直接放入页面显示,一种是做计时器用,那么当我们拥有的两个变量,在 bing 和 4sq 上是这样的:

var obj = JSON.parse(response);

// bing
var stamp = obj.DateTime; // 返回 2011-10-29T09:35:00Z
// 显示出来也看得出是一个日期
// 但不能做计数器用,需要转换,这种格式 JS 还不好转

// 4sq
var stamp = obj.createdAt; // 返回 
// 显示出来需要用 JS 转 1320296464,因为是秒数,可以方便转成各种格式
// 可直接用于计数

显然,这种方式更灵活。API 最好支持 JS 直接调用/使用。

三、把复杂的变量放在 URL 参数上

这个如上面第一点所提到的,层级导致代码的难度增大,不如放成参数;这样还可以保证 API 接口的简洁和少量,而实现同样多的功能。

然后,或者你会像我一样,再一次阅读。然后去迭代/优化你的 API。

13 Comments

  1. lepture
    lepture August 25, 2012 at 10:14 am . Reply

    语义上来说,我一般的设计是:

    1. /dogs
    2. /dog/1234

    另外,用 speakerdeck 吧, slideshare 太烂了。

    1. sofish
      sofish August 25, 2012 at 10:57 am . Reply

      考虑一下:

      var dogs = ['dog1', 'dog2', ..., 'dogN'];

      var n = dogs[n-1];

      其实,dogs/n 代表的更应该是 dogs 中的 dogN,是不是 /dogs/1234 优于 /dog/1234?

      再从另外一面上,抛开语义,还有另外一层,而且是比语义更重要的一层,是 API 的简洁。/dogs 和 /dogs/1234 统一的 URL 使 API 更简洁。我偏向于喜欢作者的方式

      1. lepture
        lepture August 25, 2012 at 11:17 am . Reply

        这样理解也行,不过和我的思维不一致。

  2. 飞扬
    飞扬 August 25, 2012 at 10:59 am . Reply

    写的不错,看来你收获不小。 @lepture:disqus 在restful设计中,collection都需要用复数来表示

    1. lepture
      lepture August 25, 2012 at 11:16 am . Reply

      我说过了,语义上来说。我并没有把 restful 奉为圭臬。

      1. Freetao
        Freetao August 25, 2012 at 11:43 am . Reply

        支持一个,虽然个人比较赞成作者的方式,但我也认为技术领域无对错。

      2. 秋爱
        秋爱 August 29, 2012 at 11:52 am . Reply

        我专门百度了一下 圭臬……学习了….

  3. XiNGRZ
    XiNGRZ August 25, 2012 at 1:43 pm . Reply

    十分赞同!

  4. MK2
    MK2 August 25, 2012 at 6:18 pm . Reply

    太喜欢了。

    1. tenlywu
      tenlywu August 26, 2012 at 5:39 am . Reply

      这里居然遇见你。。

  5. Liang Yongrui
    Liang Yongrui September 4, 2012 at 2:14 am . Reply

    如何处理More Verbs, 比如email 除了CRUD外,还有Send,这个verb如何在REST API中表达

  6. ada
    ada September 8, 2012 at 9:24 am . Reply

    看看 twitter,facebook,sina,renren开发的api,都是说自己是restful api,但有多少是“符合”restful规范

Post Comment