当 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.

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

举个例子:

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

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

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

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

或者 /service/activities/:id/activation 这样的名词形式的 url 就行了。

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

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

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

这次我犯的错误是: