当 Backbone.js 遇上非 CRUD API

最近使用 Backbone.js 开发客户端时发现了一个看似很小,实则非常麻烦的问题:

如何与非 CRUD 式的 RESTFul API 交互。

举个例子,有一个活动 activity
模型,它可以被开启或者停止,有两种解决方案:

  1. 使用 put 或者 patch,比如 /service/activities/:id 并传递参数 {status: :active}
  2. 使用额外的 endpoint, 比如 /service/activities/:id/active

如果使用第一种方式,那么就是标准的 CRUD 式的 API,Backbone.js
默认就支持。

但是实际上开启一个活动是一个比较大的动作,可能需要调用其他模块的功能,不是简单的改一个字段。

因此从 api 设计的角度我是倾向于使用第二种方式的。可是奇怪的是关于 Backbone.js 调用 CRUD 以外 的 API 的文章非常少,我翻遍 google 就勉强找到了这篇。通过临时改变 model 的 url 属性来实现的。 并没有太让我满意。

正好晚上向 vincent 请教了一下这个问题,因为讨论的重点是 api 设计而不是 backbone 的具体实现, 反而开阔了我的思路。

重新复习了一下 RESTful 的相关知识,发现了一个常见误区:

最常见的一种设计错误,就是URI包含动词。因为"资源"表示一种实体,所以应该是名词,URI不应该有动词,动词应该放在HTTP协议中。

对于这个观点争论不少,毕竟类似于 /service/activities/:id/active 这样的设计还是很常见的。 这个 AOE 范围有点大。。。stackoverflow上有个讨论可以参考。其中我比较赞同的一点是:

In general, when you think you need more verbs, it may actually mean
that your resources need to be re-identified.

这里 提出的三种解决方案也可以参考。

举个例子:

print_book_example

你是想打印一本书,还是发起一个打印的订单请求?

一样的道理,我是要激活一个活动,还是发起一个激活活动的请求?

这个例子中,使用了名词代替动词。一旦变成了名词,我立马想到可以新建一个 model。

这下,只要创建一个 Backbone.js 的模型 startActivity, 把它的 url 指向
/service/activities/:id/active

当发起请求后(激活由于业务逻辑的限制,未必就会成功),后续的逻辑就可以都放在这个 startActivity 对象中。

如果激活失败就显示错误信息,成功则通知 activity 模型重新渲染自己的视图。

这样结构更加清晰。这未必是最好的解决方案,但是不失为一种不错的思路。

这次我犯的错误是,一开始总是从 Backbone.js 的方面找问题。想要把 Backbone.js 变成 一把锤子来解决这个钉子。二是对 RESTful 这样比较基础的知识的理解还是不够,否则思路也不会这么局限。

推荐阅读更多精彩内容