warmhug

技术成长、工作感悟、教训反思

Rest 是否也要死了?

去年的这个时候,前端社区里在为 React.js 势头超越 Angular.js 快速崛起而欢欣鼓舞,一年过去了,React 终于变成了现在前端圈最火的东西了,如果你还不知道或没用过它,估计你就不好意思说自己是做前端的了。

而近几个月同样发生着另外一个有趣又类似的大事:在后端被广泛使用的 Rest (RESTful API),开始受到各种质疑,特别是声称解决或替代 Rest 的方案如 GraphQLFalcor 的出现并得到越来越多的关注,似乎问题来了:Rest 是否也像 Angular.js 那样,要死了?

Rest 有什么问题?列下别人提到的一些:

一:资源粒度

通过一个请求加载所有的信息同时使得我们的 Rest 资源保持隔离之间的冲突。 ref: GraphQL

个人对这个的理解:Rest 的资源是拆分的“比较细的”,但应用里是想通过一个请求直接拿到尽可能多的数据,但对于 Rest 一次请求只能拿到某一个资源,这样就产生冲突了。

但对于这个问题,资源的 embeded 其实是可以解决的。目前对于那些有类似 “外键” 关联的资源(如一个学生数据库表里关联着老师的id),客户端发一个学生资源的请求、带上关联老师详情的 query 信息,就能把老师的详细信息关联查出来,一并发送给客户端。

二:实际场景

常见的图片瀑布流应用(无尽列表),随着用户鼠标滚动,要不停的发送请求,显示新的图片并获得相应的图片详细信息。

这种场景下,如果设计不当,例如在用户滚动的时候,才去加载需要的细粒度资源。那么假如获取一张图片的详细信息时,就需要发送至少 5 个资源请求,用户随便拖动下滚动条,就可能需要显示10张图片,这样一下子至少要发送 10*5 个资源请求,很明显这是不能忍的也是不对的。

这类场景正确的做法是只需要发送一次请求,返回一个列表数据,并在里边包含各种子资源即可。只是如果资源粒度比较细的话,子资源需要被 embed 进来就需要拼接不少参数,比较麻烦。

举个实例:

资源列表如:
[
{
  id: 1
  name: "card",
  boxshot: "http://xx",
  rating: 5,
  bookmark: 2343,
  director: "david",
  // more fields
}
...
]

Rest 请求列表资源
/list?rowOffset=0&rowSize=5&colOffset=5&colSize=15&titleprops=name,boxshot

RPC 风格:
/list?pageSize=10x15&titleprops=name,boxshot

请求单个资源部分信息:
/list/123?props=name,rating,bookmark

以上可以看出,Rest 请求列表资源时,可能需要加很多参数,还不如 RPC 风格来的简单。这也大概正是 Falcor 作者要解决的问题,把 RPC 里的优点拿过来,更简单的组合资源。

另外这里引用 Falcor 作者提到的 Rest 的设计初衷(视频):

The REST interface is designed to be efficient for large-grain hypermedia data transfer, optimizing for the common case of the Web, but resulting in an interface that is not optimal for other forms of architectural interaction.

三:Rest API 的设计并不容易

一开始我们做系统分析时,可能还是比较容易的划分出有哪些资源,对资源粒度划分也可能没那么难。但把这些资源组合或规划起来、对外提供 API 时,可能就没那么容易了,会有让人纠结的地方。如:

  • 资源与子资源的组合和划分
    • 有一些资源有明确的父子关系,但很多资源并没有
    • 很多资源可能是平级的关系,但需要互相联系起来
    • 具体例子如:
      GET /cars/711/drivers/ 
      Returns a list of drivers for car 711
        
      GET /cars/711/drivers/4 
      Returns driver #4 for car 711
      
  • 一些动作类资源的恰当处理
    • 常见的如登陆、验证等

这些问题可能远远地看起来似乎没什么,但实际去设计去做时,还是很容易搞不好的。

Rest 自身能否解决问题?

一开始 Rest 的出现,是为了解决 Rpc 的问题。Rpc 方式下,应用只有一个端点(endpoint),导致紧耦合、不易缓存。

Rpc 和 Rest 的基本区别:

SOAP Web API采用RPC(面向方法Remote Procedure Call)风格,它采用面向功能的架构,所以在设计之初首先需要考虑的是提供怎样的功能。

RESTful Web API采用ROA(面向资源Resouce Oriented Architecture)架构,所以在设计之初首先需要考虑的是有哪些资源可供操作。

但如果对 Rest 的实现并不充分的话,也会不能明显感受到其好处。这里引用下 richardson模型,其中介绍了怎么更充分的实现 Rest:

  • Level 1 Resources (解决了 Rpc 问题)
  • Level 2 Http verbs (使用http verbs来对各种资源进行crud操作,使应用接口更加的统一)
  • Level 3 Hypermedia Controls
    • level 3 带来了 service discoverablility 和 self-documenting。举例就像访问了某一个最顶层资源,它会返回一个它的子资源列表地址;再访问其中某个子资源,这个子资源又返回它的子资源或关联资源的地址;依次下去甚至能发现整个应用的所有资源地址。实例如 GitHub-API

文中作者只提到了第三层级,可惜没有第四层级了。之前我们提到过资源的 embeded,文中也并没提到,但这个也只是父子资源的 embeded。试想假如可能的话,让所有资源都能自由的互相 embeded ,或者自由的做关联,那是否算更进了一步。

即便做到如此,Rest 也还是有其让人觉得不爽的地方。GraphQL / Falcor 这类方案,虽说都是宣称解决了 Rest 的一些问题,但它们真的是更上一层楼?恐怕也不见得。但新东西的兴起总会伴随着各种质疑,是好是坏,待以后实践证明。


November 07, 2015 | by warmhug | js