#### Representational State Transfer
表述性状态转移
REST(Representational State Transfer表述性状态转移)是一种针对网络应用的设计和开发方式,可以降低开发的复杂性,提高系统的可伸缩性。
REST提出了一些设计概念和准则:
1. 网络上的所有事物都被抽象为资源(resource);
2. 每个资源对应一个唯一的资源标识(resource identifier);
3. 通过通用的连接器接口(generic connector interface)对资源进行操作;
4. 对资源的各种操作不会改变资源标识;
5. 所有的操作都是无状态的(stateless)。
对于当今最常见的网络应用来说,`resource identifier`是`url`,`generic connector interface`是`HTTP`,第4条准则就是我们常说的url不变性。这些概念中的`resouce`最容易使人产生误解。`resouce`所指的并不是数据,而是`数据+特定的表现形式`(representation),这也是为什么REST的全名是`Representational State Transfer`的原因。
举个例子来说,“本月卖得最好的10本书”和“你最喜欢的10本书”在数据上可能有重叠(有一本书即卖得好,你又喜欢),甚至完全相同。但是它们的representation不同,因此是不同的resource。
REST之所以能够简化开发,是因为其引入的架构约束,比如Rails 1.2中对REST的实现默认把controller中的方法限制在7个:index、show、new、edit、create、update和 destory,这实际上就是对CURD的实现。
更进一步讲,Rails(也是当今大部分网络应用)使用HTTP作为`generic connector interface`,HTTP则把对一个url的操作限制在了4个之内:`GET`、`POST`、`PUT`和`DELETE`。
REST之所以能够提高系统的可伸缩性,是因为它强制所有操作都是stateless的,这样就没有context的约束,如果要做分布式、做集群,就不需要考虑context的问题了。同时,它令系统可以有效地使用pool。
REST对性能的另一个提升来自其对client和server任务的分配:server只负责提供resource以及操作resource的服务,而client要根据resource中的data和representation自己做render。这就减少了服务器的开销。
既然REST有这样的好处,那我们应该做什么呢?
我觉得我们应该思考两个问题:
1. 如何使用REST;
2. REST和MVC的关系。
第一个问题假设REST是我们应该采用的架构,然后讨论如何使用;第二个问题则要说明REST和当前最普遍应用的MVC是什么关系,互补还是取代?
我们先来谈谈第一个问题,如何使用REST。我感觉,REST除了给我们带来了一个崭新的架构以外,还有一个重要的贡献是在开发系统过程中的一种新的思维方式:通过url来设计系统的结构。根据REST,每个url都代表一个resource,而整个系统就是由这些resource组成的。因此,如果url是设计良好的,那么系统的结构就也应该是设计良好的。对于非高手级的开发人员来说,考虑一个系统如何架构总是一个很抽象的问题。敏捷开发所提倡的Test Driven Development,其好处之一(我觉得是最大的好处)就是可以通过testcase直观地设计系统的接口。比如,在还没有创建一个class的时候就编写一个testcase,虽然设置不能通过编译,但是testcase中的方法调用可以很好地从class使用者的角度反映出需要的接口,从而为 class的设计提供了直观的表现。这与在REST架构中通过url设计系统结构非常类似。虽然我们连一个功能都没有实现,但是我们可以先设计出我们认为合理的url,这些url甚至不能连接到任何page或action,但是它们直观地告诉我们:系统对用户的访问接口就应该是这样。根据这些url,我们可以很方便地设计系统的结构。
让我在这里重申一遍:REST允许我们通过url设计系统,就像`Test Driven Development`允许我们使用`testcase`设计`class接口`一样。
url有这样的好处,那我们就着重讨论一下如何设计url。
网络应用通常都是有 hierarchy 层级的,像棵大树。我们通常希望url也能反映出资源的层次性。
比如对于一个blog应用:/articles表示所有的文章, /articles/1表示id为1的文章,这都比较直观。遗憾的是,网络应用的资源结构永远不会如此简单。因此人们常常会问这样一个问题:
RESTful的url能覆盖所有的用户请求吗?比如,login如何RESTful?search如何RESTful?
从REST的概念上来看,所有可以被抽象为资源的东东都可以使用RESTful的url。因此对于上面的两个问题,如果login和search可以被抽象为资源,那么就可以使用RESTful的url。
search比较简单,因为它只需要显示搜索结果,返回的搜索结果,可以被抽象为资源,那么实现index方法就可以了。然而:search的关键字如何传给server?index方法显然应该使用HTTP GET,如果把关键字加到url后面,不符合REST的风格。
要解决这个问题,可以把每次search看作一个资源,因此要创建create和 index方法,create用来在用户点击“搜索”按钮是通过HTTP POST把关键字传给server,然后index则用来显示搜索结果。这样一来,我们还可以记录用户的搜索历史。
使用同样的方法,我们也可以对 login应用REST,即每次login动作是一个资源。
现在,我们来看复杂一些的东东。如何用url表达“category为ruby的 article”?一开始可能想到的是/category/ruby/articles,这种想法很直观。但是我觉得里面的category是不需要的,我们可以直接把“/ruby”理解为“category是ruby”,也就是说“ruby”出现的位置说明了它指的就是category。OK, /ruby/articles,单单从这个url上看,我们能获得多少关于category的信息呢?显然category隐藏在了url后面,这样做到底好不好,应该是仁者见仁,智者见智了。对于如何表达category这样的东西,我还没想出很好的方式,大家有什么好idea,可以一起讨论。
另外还有一种url形式,它对应到程序中的继承关系。比如product是一个父类,book和computer是其子类。那么所有产品的url应该是/products,所有书籍的url应该是/books,所有电脑的url应该是 /computers。这一想法就比较直观了,而且再次验证了url可以帮助我们进行设计的论点。
> 让我再说明一下我的想法:如果每个用户需求都可以抽象为资源,那么就可以完全使用REST。
由此看来,使用REST的关键是如何抽象资源,抽象得越精确,对REST的应用就越好。
因此,如何改变我们目前根深蒂固的基于action的思想是最重要的。
有了对第一个问题的讨论,第二个问题就容易讨论多了。REST会取代MVC吗?还是彼此是互补关系(就像AOP对于OOP)?答案是It depends!如果我们可以把所有的用户需求都可以抽象为资源,那么MVC就可以推出历史的舞台了。如果情况相反,那么我们就需要混合使用REST和 MVC。
当然,这是非常理想的论断。可能我们无法找到一种方法可以把所有的用户需求都抽象为资源,因为保证这种抽象的完整性(即真的是所有需求都可以)需要形式化的证明。而且即使被证明出来了,由于开发人员的能力和喜好不同,MVC肯定也会成为不少人的首选。但是对于希望拥抱REST的人来说,这些都没有关系。只要你开发的系统所设计的问题域可以被合理地抽象为资源,那么REST就会成为你的开发利器。
- 《Restful Web Service Cookbook》
- 简介
- 推荐序
- 第 1 章 使用统一接口
- 1.1 如何保持交互的可见性
- 1.2 何时使用 GET 方法
- 1.3 何时使用 POST 方法
- 1.4 如何使用 POST 方法创建资源
- 目录
- PHP RESTful
- 理解RESTful架构
- 深入浅出REST
- 表述性状态转移
- 无状态
- 设计指南
- RESTful API 设计指南
- RESTful API 设计最佳实践
- 测试工具
- soapUI
- 实例
- 豆瓣
- 应用
- 接口规范
- 视频
- 《Restful API实战》
- 课程目标
- 第1章 Restful是什么
- 1-1 restful简介及资源介绍
- 第2章 为什么要使用Restful
- 2-1 Restful中HTTP协议介绍
- 2-2 架构区别
- 第3章 如何使用Restful
- 3-1 restful设计要素
- 3-2 DHC Client
- 3-3 本地开发环境搭建
- 3-4 确认设计要素
- 3-5 数据库设计
- 3-6 用户业务逻辑
- 3-7 文章业务逻辑
- 3-8 restfulApi设计要素
- 3-9 初始化运行参数
- 3-10 完善用户API
- 3-11 完善文章API
- 文摘
- RESTful 接口实现简明指南
- RESTful Web 服务教程
- 模型表示(Representations)
- 资源定位
- URI中的查询参数
- 统一接口
- PUT 和 POST 的区别
- 基于 REST 的 Web 服务
- PHP Reatful
- Restful状态响应码
- 《REST API 安全设计指南》