如何创建RESTFul Web服务-阿里云开发者社区

开发者社区> 开发与运维> 正文

如何创建RESTFul Web服务

简介: 想写这篇文章很久了,这是个大话题,不是一时半会就能说清楚的。 所以准备花个一星期整理资料,把思路理清楚,然后再在Team里做个sharing:) 其实RESTFul是架构风格,并不是实现规范,也不一定非要用HTTP,但鉴于HTTP的普世性和 SOA的实现基本都基于HTTP实现。 这句话只对了前一半, 实际上REST和HTTP是息息相关的,是一种Web架构,WWW是世界最大型的分布式应用,

想写这篇文章很久了,这是个大话题,不是一时半会就能说清楚的。 所以准备花个一星期整理资料,把思路理清楚,然后再在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 协议

1. Request/Response Header

2. Status Code

3. HTTP 1.1 Standards

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


版权声明:本文内容由阿里云实名注册用户自发贡献,版权归原作者所有,阿里云开发者社区不拥有其著作权,亦不承担相应法律责任。具体规则请查看《阿里云开发者社区用户服务协议》和《阿里云开发者社区知识产权保护指引》。如果您发现本社区中有涉嫌抄袭的内容,填写侵权投诉表单进行举报,一经查实,本社区将立刻删除涉嫌侵权内容。

分享:
开发与运维
使用钉钉扫一扫加入圈子
+ 订阅

集结各类场景实战经验,助你开发运维畅行无忧

其他文章