最近我一直在阅读“Rest实践”的草稿:一本几位同事一直在努力编写的书。 他们的目的是解释如何使用Restful Web服务来处理企业面临的许多集成问题。 这本书的核心在于这样一种观点,Web以一个有效的可扩展分布式系统存在,它的工作效果非常好,这足以证明我们可以从中得到想法轻松地构建集成系统。

图1: REST的步骤

为了帮助解释一个web-style的系统的具体属性,作者使用了由Leonard Richardson开发的一种restful成熟度模型,这模型他在QCon谈话中解释过。 这个模型是理解使用这些技术的好方法,所以我想我会用自己的语言来解释它。 (这里的协议示例只是说明性的,我觉得不值得编码和测试它们,所以细节可能会有问题)

Level 0

该模型的起点是使用HTTP作为远程交互的传输系统,但不使用任何Web的机制。 基本上你在这里做的是使用HTTP作为一般基于远程过程调用的远程交互机制的隧道机制。

图2: Level 0交互例子

假设我想和我的医生预约一下。 我的预约软件首先需要知道我的医生有哪些时间点是可预约的,所以它请求医院预约系统来获取这些信息。 在Level 0的场景中,医院会用URI暴露一个服务端。 然后我将该请求的详细信息发送到该服务端。

POST /appointmentService HTTP/1.1
[various other headers] <openSlotRequest date = "2010-01-04" doctor = "mjones"/>

然后,服务端将返回给我这些信息的文档

HTTP/1.1 200 OK
[various headers] <openSlotList>
<slot start = "1400" end = "1450">
<doctor id = "mjones"/>
</slot>
<slot start = "1600" end = "1650">
<doctor id = "mjones"/>
</slot>
</openSlotList>

我的例子使用XML,但实际上内容可以是任何格式:JSON,YAML,键值对或任何自定义格式。

我的下一步是预约看医生,我可以通过将文档发送到服务端来再次执行。

POST /appointmentService HTTP/1.1
[various other headers] <appointmentRequest>
<slot doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
</appointmentRequest>

如果一切顺利,我得到一个答复说我的预约成功。

HTTP/1.1 200 OK
[various headers] <appointment>
<slot doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
</appointment>

如果有问题,说别人已预约,那么我会在回复主体中收到一些错误信息。

HTTP/1.1 200 OK
[various headers] <appointmentRequestFailure>
<slot doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
<reason>Slot not available</reason>
</appointmentRequestFailure>

到目前为止,这是一个简单的RPC风格系统。 这很简单,因为它只反复发送和接收旧格式XML(POX)。 如果您使用SOAP或XML-RPC,它基本上是相同的机制,唯一的区别是将XML消息包装在某种信封(envelope)中。

Level 1 - 资源

RMM模型“Rest荣耀”第一步是引入资源概念。 所以不是将我们的所有请求都发送给一个单一的服务端,我们现在开始讨论个体资源。

图3: Level 1资源

所以上面的询问请求,我们可能有一个指定医生的资源。

POST /doctors/mjones HTTP/1.1
[various other headers] <openSlotRequest date = "2010-01-04"/>

答复带有相同的基本信息,但每个时间段现在是可以单独寻址(查询)的资源。

HTTP/1.1 200 OK
[various headers]
<openSlotList>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
<slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

通过具体资源预约,即可预约具体时间段看医生。

POST /slots/1234 HTTP/1.1
[various other headers] <appointmentRequest>
<patient id = "jsmith"/>
</appointmentRequest>

如果一切顺利,我会得到类似的回复。

HTTP/1.1 200 OK
[various headers] <appointment>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
</appointment>

这与传统RPC区别是,如果任何人需要做任何关于预约看医生的事情,比如做测试,他们首先要得到这个预约资源,可能会是像http://royalhope.nhs.uk/slots/1234/appointment的URI ,提交请求到这URI就可预订该资源。

像我这样熟悉面向对象的人,这就像对象身份的概念。 相当于在以太网调用函数并传递参数,我们在一个特定对象上调用一个方法为其他信息提供参数。

Level 2 - HTTP动词

在Level 0和Level 1我使用HTTP POST动词做交互,但有些人使用GETs或其它。 在这些级别上,它们没有太大的区别,它们都被用作隧道机制,允许您通过HTTP隧道传输交互。 Level 2更进一步,使用HTTP动词尽可能接近它们在HTTP协议中的使用语义。

图4: Level 2HTTP动词

对于读取我们的时间段列表,这意味着我们要使用GET。

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1 Host: royalhope.nhs.uk

答复与POST一样

HTTP/1.1 200 OK

[various headers]

<openSlotList>

<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>

<slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>

</openSlotList>

在Level 2,使用GET这样的请求至关重要。 HTTP将GET定义为安全操作,即对任何状态都没有任何重大更改。 这允许我们以任何顺序安全地调用GETs次数,并且每次获得相同的结果。 这样做的一个重要结果是它允许任何参与者路由请求可以使用缓存,这是web有良好性能的一个关键因素。 HTTP包括支持缓存的各种措施,可以由通信中的所有参与者使用。 通过遵循HTTP的规则,我们可以利用该功能。

要预约看医生,我们需要一个HTTP动词来改变状态,一个POST或一个PUT。 我会使用我之前用过的POST。

POST /slots/1234 HTTP/1.1
[various other headers] <appointmentRequest>
<patient id = "jsmith"/>
</appointmentRequest>

使用POST和PUT之间的取舍超出本文范围,也许我会有一天单独撰写一篇文章。 但是我想指出,有些人错误地在POST / PUT和创建/更新之间建立了对应关系。 它们之间的选择与此有所不同。

即使我使用与Level 1相同的提交请求,远程服务的响应方式也有重大差异。 如果一切顺利,服务将回复201的返回码,以表明世界上有一个新的资源。

HTTP/1.1 201 Created

Location: slots/1234/appointment

[various headers]

<appointment>

<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>

<patient id = "jsmith"/>

</appointment>

201响应包括具有位置属性的URI,客户端将来可以使用该属性来获取该资源的当前状态。 这里的响应还包括该资源保存的表示,以便客户端可以额外使用。

如果出现问题,还有其他人预约了该医生,还有另一个返回码区别。

HTTP/1.1 409 Conflict
[various headers] <openSlotList>
<slot id = "5678" doctor = "mjones" start = "1600" end = "1650"/>
</openSlotList>

此响应的重要部分是使用HTTP返回码来指示出现问题。在这种情况下,409似乎是一个很好的选择,表明其他人已经以不兼容的方式更新了资源。而不是使用200的返回码,而是包含错误响应,在Level 2,我们明确地使用某种类似的错误响应。由协议设计者决定要使用哪些代码,但如果出现错误,应该有非2xx响应。Level 2引入了HTTP动词和HTTP返回码。

这里有不一致的地方。 REST主张使用所有的HTTP动词。他们也证明了他们的做法,REST正在尝试从web成功实践中学习。但在互联网实践中并没有使用PUT或DELETE。有很多明智理由使用PUT和DELETE,这说明web只是REST学习之一不是全部。

Web成功的关键因素是,安全(例如GET)和非安全操作完全分离,以及使用状态代码来帮助传达您遇到的各种错误。

Level 3 - 超媒体控制

最后介绍您经常听到的HATEOAS(超文本作为应用程序状态引擎)看起来有点丑陋首字母缩写。 它解决了如何从时间段列表中如何预约看医生的问题。

图5: Level 3超媒体控制

我们在Level 2发送的第一个GET

GET /doctors/mjones/slots?date=20100104&status=open HTTP/1.1
Host: royalhope.nhs.uk

响应多了一个新的元素

HTTP/1.1 200 OK
[various headers] <openSlotList>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450">
<link rel = "/linkrels/slot/book"
uri = "/slots/1234"/>
</slot>
<slot id = "5678" doctor = "mjones" start = "1600" end = "1650">
<link rel = "/linkrels/slot/book"
uri = "/slots/5678"/>
</slot>
</openSlotList>

现在每个时间段都有一个链接元素,其中包含一个URI来告诉我们如何预约看医生。

超媒体控件的要点是,他们告诉我们下一步可以做什么,以及我们需要操作资源的URI。 而不是我们必须知道在哪里发布我预约医生请求,响应中的超媒体控件告诉我们如何做。

POST会再次重复Level 2请求

POST /slots/1234 HTTP/1.1
[various other headers] <appointmentRequest>
<patient id = "jsmith"/>
</appointmentRequest>

响应包含一些超媒体控件,用于不同的事情。

HTTP/1.1 201 Created
Location: http://royalhope.nhs.uk/slots/1234/appointment
[various headers] <appointment>
<slot id = "1234" doctor = "mjones" start = "1400" end = "1450"/>
<patient id = "jsmith"/>
<link rel = "/linkrels/appointment/cancel"
uri = "/slots/1234/appointment"/>
<link rel = "/linkrels/appointment/addTest"
uri = "/slots/1234/appointment/tests"/>
<link rel = "self"
uri = "/slots/1234/appointment"/>
<link rel = "/linkrels/appointment/changeTime"
uri = "/doctors/mjones/slots?date=20100104@status=open"/>
<link rel = "/linkrels/appointment/updateContactInfo"
uri = "/patients/jsmith/contactInfo"/>
<link rel = "/linkrels/help"
uri = "/help/appointment"/>
</appointment>

超媒体控件的一个明显好处是允许服务器在不破坏客户端的情况下更改其URI方案。只要客户端查找“addTest”链接URI,那么服务器团队可以处理除初始入口URI之外的所有URI。

另一个好处是它帮助客户端开发人员导向下一步。这些链接为客户端开发人员提供了下一步可能发生的情况。但它没有提供所有信息:“最新”和“取消”控件指向相同的URI - 可是要知道它们一个是GET,另一个是DELETE。但是至少清楚更多后续信息的切入点和从协议文档找到类似URI。

类似地,它允许服务器团队通过在响应中添加新链接来发布新功能。如果客户端开发人员正在关注未知链接,那么这些链接可以进一步导向一个触发功能。

关于如何表示超媒体控件没有绝对的标准。我在这里做的是使用REST实践团队中的当前建议,这是跟随ATOM(RFC 4287),我使用一个<link>元素与目标URI的uri属性和一个rel属性来描述那种关系。一个众所周知的关系(例如自己就是引用元素)应该是公开的,任何特定服务器都是一个完全URI。 ATOM提出一个著名的相关类型定义就是链接关系注册表。我写这些是有限的,这些已定义在ATOM,ATOM通常被认为是Level 3 restfulness的领导者。

分层的含义

我应该强调,RMM模型是用来考虑REST元素很好的方式,而不是REST层次的定义。 Roy Fielding已经明确指出,3级RMM是REST的前提条件。与软件中许多术语一样,REST有很多定义,但是由于Roy Fielding创造了这个术语,他的定义应该比其它的更重。

我认为这RMM模型有用的是,它提供了一个很好的一步一步的方式来理解restful思想背后的基本想法。因此,我认为它是帮助我们了解概念的工具,而不是在某种评估机制中使用的工具。我不认为我们有足够的例子说明采取restful方法整合系统是正确,我认为这是一个非常有吸引力的方法,在大多数情况下我会推荐使用。

谈到这里想起Ian Robinson,他强调,发现Leonard Richardson首先提出这模型里有吸引力的地方的是,公共设计技术中的关系,。

  • Level 1通过分割和征服来解决处理复杂性的问题,将大型服务端分解成多个资源。
  • Level 2引入了一套标准动词,以同样的方式处理类似的情况,消除不必要的变化。
  • Level 3引入可发现性,提供一种方式使协议更加自我记录。

这是一种模型,可以帮助我们考虑我们想要提供的各种HTTP服务,并制订人们想与之进行交互的期望。

注:本文翻译自Martin Fowler-Richardson Maturity Model

翻译版权所有转载注明出处

[翻译]Restful Web服务模型的更多相关文章

  1. 在GlassFish应用服务器上创建并运行你的第一个Restful Web Service【翻译】

    前言 本人一直开发Android应用,目前Android就业形势恶劣,甚至会一路下滑,因此决定学习服务器开发.采用的语言是java,IDE是Intellij,在下载Intellij的同时看到官网很多优 ...

  2. [翻译]Spring MVC RESTFul Web Service CRUD 例子

    Spring MVC RESTFul Web Service CRUD 例子 本文主要翻译自:http://memorynotfound.com/spring-mvc-restful-web-serv ...

  3. 我所理解的RESTful Web API [Web标准篇]

    REST不是一个标准,而是一种软件应用架构风格.基于SOAP的Web服务采用RPC架构,如果说RPC是一种面向操作的架构风格,而REST则是一种面向资源的架构风格.REST是目前业界更为推崇的构建新一 ...

  4. 我所理解的RESTful Web API [设计篇]

    <我所理解的RESTful Web API [Web标准篇]>Web服务已经成为了异质系统之间的互联与集成的主要手段,在过去一段不短的时间里,Web服务几乎清一水地采用SOAP来构建.构建 ...

  5. 使用Java创建RESTful Web Service

    REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移).2000年Roy Fielding博士在他的博士论文“Architectural Sty ...

  6. 使用Java创建RESTful Web Service(转)

    REST是REpresentational State Transfer的缩写(一般中文翻译为表述性状态转移).2000年Roy Fielding博士在他的博士论文“Architectural Sty ...

  7. 【ASP.NET MVC 学习笔记】- 19 REST和RESTful Web API

    本文参考:http://www.cnblogs.com/willick/p/3441432.html 1.目前使用Web服务的三种主流的方式是:远程过程调用(RPC),面向服务架构(SOA)以及表征性 ...

  8. RESTful Web服务的操作

    1.首先我们说一下Http协议是无状态的 HTTP协议是无状态的,我们看到查到的用到的返回404,500,200,201,202,301.这些不是HTTP协议的状态码. 是HTTP的状态码,就是HTT ...

  9. [译]Spring Boot 构建一个RESTful Web服务

    翻译地址:https://spring.io/guides/gs/rest-service/ 构建一个RESTful Web服务 本指南将指导您完成使用spring创建一个“hello world”R ...

随机推荐

  1. 20155208徐子涵 2016-2017-2 《Java程序设计》第4周学习总结

    20155208徐子涵 2016-2017-2 <Java程序设计>第4周学习总结 教材学习内容总结 第六章 继承与多态 继承 继承作为面向对象的第二大特征,基本上就是避免多个类间重复定义 ...

  2. 2017年秋软工-PSP总结报告

    一.回顾1 回顾本学期第一次作业[https://edu.cnblogs.com/campus/nenu/SWE2017FALL/homework/876]. ==>本学期我的第一次作业博客[h ...

  3. NET Core + Ocelot + IdentityServer4 + Consul

    .NET Core + Ocelot + IdentityServer4 + Consul 基础架构实现 先决条件 关于 Ocelot 针对使用 .NET 开发微服务架构或者面向服务架构提供一个统一访 ...

  4. acm 2057

    ////////////////////////////////////////////////////////////////////////////////#include<iostream ...

  5. 使用fastjson解析数据后导致顺序改变问题

    在开发过程中遇到一个问题,服务器经过排序返回后的字符串数据使用fastjson解析后,数据顺序发生变化,引起业务异常. 解决办法: 1.解析时增加参数不调整顺序 JSONObject responde ...

  6. controller层,service层,dao层(main函数,子函数,子的子函数)

    controller层相当于main函数————————————————————————————————————————————————————@RequestMapping("/query ...

  7. Centos7解决中文乱码问题

    查看当前文字编码,发现都是en_US.UTF-8,也就是说还不能完美支持中文编码,如果是zh_CN.UTF-8,那就说明能够比较完美支持中文编码了(我猜的) [root@biaopei ~]# loc ...

  8. MySQL--线程池(Thread Pool)

    ================================================================= 线程池技术 在MySQL社区版中,MySQL使用one-thread ...

  9. What is Zeebe?

    转自:https://zeebe.io/what-is-zeebe/ Zeebe is a workflow engine for microservices orchestration. This ...

  10. python获取代理IP并测试是否可用

    # coding: utf-8 import urllib2 import re import time def getDL(page): url = 'http://www.xicidaili.co ...