Arnon Rotem-Gal-Oz 在他的新博文"CRUD 不适合 REST"中这样开篇:
表面上看起来它们很相配(无论从技术上还是架构上),然而,一旦深入了解,你就会发现它们并不相配。
今天,REST 架构风格的常见实现是基于 HTTP 协议及其相应动作(如 POST,GET, PUT 和 DELET)的。而且,这些动作经常会被实现者映射为 CRUD 术语——Create,Read,Update 和 Delete。典型的做法是 1 对 1 映射。
- GET 通常被映射为 CRUD 中的 Read,只是 GET 还提供了超越开箱即用(Out-of-the-box)的 SELECT(即 Read)映射的一些特征。
- DELETE 通常被映射为 CRUD 中的 Delete
- PUT 通常映射为 CRUD 中的 Update,只不过增加了一些限制:
- PUT 替换的对象是整个资源,而 Update 可以替换一部分。
- PUT 可以用于创建一个资源(当客户端设定 RUI 时)
- POST 通常被映射为 CRUD 中的 Create,但它仅支持创建子资源,可是 POST 支持对资源的部分更新。
Arnon 认为:
HTTP 动作更加面向文档而不是数据库,至少在谈到 HTTP 动作时,执行 Update,Delete 和 Create 新资源采用的做法和 CRUD 在数据库的世界里的做法是不完全一样的。
然而,CRUD 不适合于 REST 的最大原因是架构上的,REST 的核心是使用超媒体实现的协议状态机。Arnon 引用 Tim Ewald 的话:
……这是我所理解的。每一种交互协议都有一个状态机,有些很简单,有些则比较复杂。当你通过 RPC 来实现协议时,你是在创建用于修改交互状态的方法。从交互端点看来,状态是由一个黑盒子维护的。由于协议状态是隐藏的,所以很容易出错。比如,你很有可能在初始化之前就调用某个流程。很长一段时间里,人们一直通过为接口的类型信息下注解的方式来寻找避免这类问题出现的方法,但是我从没有看到任何主流的解决方法。事实上,协议的状态隐藏在方法调用的背后,在方法调用的过程中隐式地修改状态,这个事实更增了版本控制的趣味。
REST 的精髓是通过 URI 来显式描述协议的状态。协议状态机的当前状态是由最近一次操作的 URI 以及该操作获取到的状态描述决定的。若要修改状态,那么就对 URI 进行相应的目标状态的操作,使资源呈现期待的新状态。状态描述包括指向其他状态的链接(状态图中的弧线),通过这些链接可以从当前状态移动到目标状态。基于浏览器的应用就是这么工作的,而且你的应用程序协议没有理由不能那样工作。(ATOM 发布协议是一个经典的状态机例子,尽管它被经常认为是关于实体的,而不是状态机的)
接着 John Evdemon&rsquo 的文章中所阐述的为什么 CRUD 服务是 SOA 的反模式,Arnon 描述了 CRUD REST 的缺点:
- 限制了服务的整体概念——没有业务逻辑。
- 暴露了内部的数据库结构,或者数据接口,而不是仔细考虑后的接口。
- 鼓励直通服务和数据的做法。
- 建立的是块(Blob)服务(数据源)
- 鼓励细小的多重服务(为块服务定义多重接口),而忽略了分布式计算的若干谬误。
- 仅仅是“批着羊皮”的 C-S 结构。
Arnon 在博文结尾的时候再次强调,仅仅采用诸如 HTTP,XML,JASON 等标准(尽管他们很有用)还不能构成 REST,而只有采用了 REST 架构才算真正的 REST。
这篇博文的重要性在于它提醒到:REST 和 SOA 类似,它不是一组标准和流行的 API,而是一种架构模型,这才是需要去理解和遵循的。
查看英文原文: Is CRUD Bad for REST?
评论