想写这篇文章很久了,这是个大话题,不是一时半会就能说清楚的。 所以准备花个一星期整理资料,把思路理清楚,然后再在Team里做个sharing:)
其实RESTFul是架构风格,并不是实现规范,也不一定非要用HTTP,但鉴于HTTP的普世性和 SOA的实现基本都基于HTTP实现。 这句话只对了前一半, 实际上REST和HTTP是息息相关的,是一种Web架构,WWW是世界最大型的分布式应用,而其实现就是基于REST的web架构的设计标准,REST架构的提出者(Roy T. Fielding 2000年在他的博士论文中提出REST的架构风格),Roy
本身也是web标准(URI, HTTP, HTML) 的制定者,以及Web Server (Apache)的实现者,并且一直用REST的思想指导着标准制定,可以说HTTP就是该架构风格的一个典型应用,所以提到REST,就默认是用HTTP作为通信协议了。
一、HTTP 协议
2. Status Code
4. 中文扫盲
二、什么是REST
REST (REpresentational State Transfer,表述性状态转移) 。REST 指的是一组架构约束条件和原则。满足这些约束条件和原则的应用程序或设计就是RESTful。
REST 定义了一组体系架构原则,您可以根据这些原则设计以系统资源为中心的 Web 服务,包括使用不同语言编写的客户端如何通过 HTTP 处理和传输资源状态。 如果考虑使用它的 Web 服务的数量,REST 近年来已经成为最主要的 Web 服务设计模式。 事实上,REST 对 Web 的影响非常大,由于其使用相当方便,已经普遍地取代了基于 SOAP 和 WSDL 的接口设计。
REST常常被视为是对传统的RPC(CORBA)和Web Services(SOAP,WSDL)的轻量级架构实现的替代,虽然相比较而言,REST要简单的多,但同时其功能又是足够强大和完备的,基本上没有什么应用WS可以做,而RESTFul架构做不了的。其实WWW(万维网)本身就是基于HTTP的,可以看成是RESTful的一个成功应用的典范。Web作为世界上最大,最成功的分布式系统,其背后的原理正是REST。
REST关键原则
- 为所有“事物”定义ID
- 将所有事物链接在一起
- 使用标准方法
- 资源多重表述
- 无状态通信
http://www.infoq.com/cn/articles/rest-introduction
三、REST开发指导
0. 为每个可被标识的资源都分配一个明显的全局唯一的ID,使其在Web范围内是可寻址的(Addressable)。
好处:
一、可寻址使你的服务的各个重要方面都可以被外界访问。 URI可以作为每个重要方面的一个唯一标示符。而URI是可以被搜藏,可在应用中传递,可指代实际资源。
试想下,我在淘宝上看到一件漂亮的衣服,想分享给我的好友,如果你当前页面的URL不能被拷贝,粘贴,重新打开,这将是多么糟糕的设计。
二、可寻址令别人可以使用你的服务构建mashups(混搭)应用,将其用于你意想不到的场合。例如你可以使用搜索附近犯罪率的API,和google map API, 产生一个犯罪地图的mashup,这是很酷的一件事情。
1. 不要使用资源的物理URL,如"http://www.acme.com/inventory/product003.xml",而是使用逻辑URL如"http://www.acme.com/inventory/product/003",其具体的表述形式(Representation)可以通过HTTP的内容协商指定。
2. 使用通用的数据格式,虽然REST并没有限定通信具体的格式,可以是普通文本,也可以是图片、视频等多媒体格式,但是通常的Web都是面向人类的信息系统,其内容是人可读的。如果我们开发的服务是给机器调用的,最好还是选用通用格式如XML、JSON,因为常见的格式处理起来非常容易,有大量的框架和库提供支持。
3. 使用超媒体,也就是"将超媒体作为应用状态的引擎"HATEOAS ( Hypermedia as the engine of application state) ,这个架构约束所讲到的事情,在人类使用的Web上,是非常熟悉的观念,在一个典型的电子商务解决方案中,服务器生成带有链接的Web页面,引导用户进入选择商品、购买和安排发货的这个流程。
这就是超媒体的实际引用场景,但是它并没有局限于人类,计算机同样能够跟踪由状态机定义的协议。
例如,用GET获取"Production List", 我们可以在Response中只返回ProductID,让客户端自己组装URL "http://www.acme.com/product/PRODUCT_ID "
去获得额外信息,我们也可以在每一个Product Item上附上获取详细的URL,这样做的好处是获得详细信息的动作对客户端是透明的,即使其URL有什么改动,客户端代码也可以不用修改,其次,服务器端可以根据业务扩展,向客户端推广新的URL,其坏处是,客户端为了理解URL的语义,需要增加额外的代码进行处理,从而复杂度也有所增加。
其实在Richardson的REST成熟度模型中,运用超媒体,被视为是成熟度模型的Level
3,也就是最高等级。
4. 统一接口。使用标准方法,HTTP Methods
和 标准的返回状态码
好处:
一、统一接口施加的约束(GET和HEAD是安全的,PUT和DELETE是幂等的)增加了HTTP的可靠性。
二、具有和现有Web通用的标准,增加了于Web的兼容性,扩大了应用范围。
5.对浏览器友好,可以用浏览器+HTML完成所有测试,对缓存友好,利用HTTP缓存机制,提高性能和可伸缩性。例如对于GET操作,服务器可以在response的HTTP
头部中标明可缓存内容和缓存有效时间,这样客户端在对同样资源的再次请求时,就可以利用本地缓存,而不用去服务器端取数据,在提高了性能同时,也节省了网络带宽和服务器的负载,当然其前提条件是我们设计的REST API要符合HTTP语义,即GET方法不能改变资源的状态,且必须是幂等的。如果在你的REST服务中,GET请求时能改变资源状态的,使用客户端缓存很可能就会导致数据不一致的情况,也就不符合缓存使用对于语义透明性的要求。除了客户端缓存,从HTTP代理到服务器之间,很多地方都可以利用缓存,在服务器端也可以利用缓存,能否合理的使用缓存在某种意思上决定了我们能否提供高质量、高性能和高伸缩性分布式应用。
6.利用URL中的V1,V2做版本控制,当然也可以自定义的HTTP头部信息标识版本,但不够浏览器友好。尽量是我们的API是幂等的,可以减轻客户端错误处理机制,因为是幂等的,所以客户端用同样的请求多次调用服务,对系统造成的影响是一致的,这一点在出错重试时,特别有用。
7. 无状态通信
每个HTTP请求都是自包含(self-contained)和独立的,请求包含服务器实现该请求的全部信息,不依赖于之前的请求。
这样做的好处是:
一、提升服务器的可伸缩性和可用性。如果一台服务器无法处理所有请求,可以进行负载均衡,加入更多的服务器,因为请求是无状态、自包含的,所以连续两次请求不依赖于同一台服务器,那么加减服务器的数量对客户端是透明的,即使集群中的一台或几台服务器宕机了,也不会影响服务的可用性。
二、具有更高的可靠性。 若客户端请求超时,则客户端只要把请求重新发一遍即可。当然这同时也需要服务具有幂等性。
参考:
A very nice article of REST:
http://rest.elkstein.org/2008/02/what-is-rest.html
How to impl REST:
http://www.infoq.com/cn/articles/RESTSOAFuture
什么是好的RESTful API?
1. 这个API应该是对浏览器友好的,能够很好地融入web,而不是与web格格不入,好的RESTful API应该能够使用浏览器+HTML完成所有的测试。
2. API中所包含的资源和对资源的操作,应该是直观和容易理解的,并且符合HTTP协议的要求。
3.API应该是松耦合的。(有待进一步理解)
REST的耦合度比SOAP更低,因为SOAP实现仍是基于RPC调用方式,RPC方式是紧密耦合的表现;而紧密耦合的系统是无法适应Web级的规模可伸缩性的。REST
Web服务则继承了Web松散耦合的特点,客户应用通过逻辑URL访问服务,服务的实现对客户来说是完全透明的,客户程序可以对服务的实现技术、方法毫无所知。
4.API中所有的格式应该是常见的通用格式,常见的有HTML、XML、JSON,常见的格式处理起来非常容易,有大量的框架和库提供支持。
5. 使用HTTP状态码来表达各种出错情况。如果一个所谓的”RESTful API“对任何请求都返回200 OK的响应,在响应的消息体中返回出错情况信息,这种做法显然不符合”确保操作语义的可见性“这个REST架构风格的基本要求。
6. 这个API应该对HTTP缓存是友好的
HTTP协议是个分层的架构,从两端的User agent(浏览器)到Origin server(Apache 服务器)之间,可以插入很多中间组件。在整个HTTP通信链条的很多位置,都可以设置缓存 。最常见的是浏览器缓存和服务器缓存:
浏览器端,下载一个类似HTTP watch的浏览器插件,就能很容易观察到对一个网站的进行第二次访问时,很多信息如图片,JS,JSS都是从浏览器缓存中取得,而不是去服务器取,节省了很多网络开销,也减轻了服务器压力。
在服务器端,也有很多缓存技术,可以参见这篇文章Caching your REST API来了解一种典型的服务器缓存技术,大概意思就是在对资源执行安全的操作如GET时,将response放入缓存,当对资源进行不安全操作如PUT/DELETE,将该response从缓存中移除(PURGE)出去,这样保证客户端不会得到过期的response。
在使用缓存时,不管是在客户端还是服务器端,一个非常重要的原则就是就语义透明性(Semantic Transparency)。
什么是缓存的语义透明性?就是不管在客户端还是服务器端使用缓存,对一个请求来说,其使用缓存得到的响应(Response)和直接访问服务器(Origin Server)所得到响应结果,应该是一模一样的,没有任何差别,缓存对服务的访问者来说应该是透明的。
完全达到语义透明性只是一个理想状态,Web是松耦合的,所以弱一致性(Weak consistency) 是基于Web的分布式应用的特点,但是我们可以通过一些手段来增强一致性,尽量达到语义透明性。常用的方式有失效、验证、过期。
如何对RESTful API进行版本控制?
一个比较简单实用的做法是直接在URI中插入版本号,这样做允许多个版本的API并行运行。另一个做法是在HTTP请求中加入自定义头信息,标明使用的版本号。不过这个做法其实对浏览器不够友好,简单地使用浏览器+HTML无法测试。
还有对于微小改动,最好可以做到向后兼容而不改变版本号,最关键的就是在扩展时不能破坏现有的客户端,例如,变更一个参数,可以选择同时兼容新旧两种输入,或者保持老参数不动,提供一个新参数,在文档中必须做出说明,不推荐新用户继续使用之前的参数。
HTTP1.1规范中给出的动词对于设计RESTful API 够用吗?
如果资源抽象做的很好,对于某个资源的任何操作,通常都能够映射到CRUD四个类别中。CRUD四个类别对于操作资源来说,绝大多数情况下是完备的。HTTP的四个方法对CRUD的映射关系是Create-POST, Retrieve-GET, Update-PUT, Delete-DELETE。如果在资源上定义的操作过多,往往这个资源可以拆分出更多的资源。
关于API的幂等性
幂等性是系统接口对外部调用的承诺,承诺只要调用接口成功,外部多次调用对系统影响是一致的。幂等性的好处是,外部在调用系统失败后,在进行重试时,无需更改请求内容。
在大规模分布式系统中,完成一个功能,需要很多服务或者组件的协作,通常我们会用一个uniqueRequestId or dedupeId 来标识一次请求的唯一性,而在调用服务出错时,如网络超时,系统内部错误,一般都要进行重试,幂等的接口允许重试时,使用相同的dedupeId。而非幂等的接口可能需要更改dedupeId来和上一次请求做区分,增加了客户端使用服务的负担。
所以在我看来,不仅GET/DELETE需要时幂等的,所有的REST API都应该尽量做到幂等,包括POST/PUT。比如在eLedger系统里,创建journal entry(流水账)是最常用的API,如果客户POST两次一模一样的请求(最主要是dedupeId一样),eLedger每次都会返回成功的结果,只不过在第二个POST到来时,系统检测到流水已经被创建,并不会创建新的流水,只是将已创建的结果返回给客户端,从某种意义上,减轻了客户使用eLedger服务的难度,因为其不用担心因为重复POST所造成的数据不一致。
关于REST的安全性
REST对安全性并没有像基于WSDL,SOAP的Web Service的WS-Security的安全规范,主要取决于REST service自己的实现,因为REST是架设在HTTP之上的,所以在底层通信(TCP)使用SSL是自然而然的,SSL基本可以解决中间人(man in middle) 和 重放攻击(replay attack)等安全问题,那么对于应用层,所要解决的就是认证(authentication),授权(authorization),访问(access)的问题了,鉴于可扩展性和开放性的考虑,使用OAuth是一个比较好的选择,首先OAuth是W3C的标准规范,提供了统一的标准和规范,降低了客户端的学习成本,其次是它把真个验证流程划分为认证、授权、访问三个步骤,不仅能满足传统的Client-Server验证,还具备非常良好的可扩展性,使得地方
如何定义Resource
REST开发又被称作“面向资源的开发”,这说明对于资源的抽象,是设计RESTful API的核心内容。RESTful API建模的过程与面向对象建模类似,是以名词为核心的。这些名词就是资源,任何可命名的抽象概念都可以定义为一个资源。而HTTP协议并不是一种传输协议,它实际提供了一个操作资源的统一接口。利用有限的几个方法(GET/POST/PUT/DELETE)来达到资源在不同表述状态(REpresentationalState)之间的转移(Transfer),这也是REST名称的由来。
所以RESTful API建模的过程,可以看作是具有统一接口约束的面向对象建模过程。
如果发现资源上的操作过多,以至于HTTP的方法不够用,应该考虑设计出更多的资源。设计出更多资源(以及相应的URI)对于RESTful API来说并没有什么害处。
五、REST 的成熟度模型(Maturity Model)
Level 0 零级服务
单个的URI,单个的HTTP方法(通常是POST)。例如大多数基于Web Service的服务使用单个URI来标识端点(endpoint),别且使用HTTP POST来转移基于SOAP的载荷,完全忽略了HTTP动词的其余部分。
Level 1 一级服务
使用了多个URI,但是只使用了单个HTTP动词。这种基本的服务和零级之间的关键区别在于,这种级别的服务暴露出了很多逻辑上的资源。
Level 2 二级服务
使用了大量的可通过URI寻址的资源。这样的服务支持多个HTTP动词来暴露资源。包含在这个级别的是CRUD(Create Read Update Delete)服务.
Level 3 三级服务
最Web感知(Web-aware)的服务级别,支持超媒体作为应用状态的引擎的观念。即,表述包含了消费者可能感兴趣的到其他资源的URI连接,这种服务通过追踪资源来引导消费者,结果是引起应用状态的迁移。
http://martinfowler.com/articles/richardsonMaturityModel.html