码迷,mamicode.com
首页 > Web开发 > 详细

[翻译]Restful Web服务模型

时间:2017-04-10 19:37:39      阅读:241      评论:0      收藏:0      [点我收藏+]

标签:fit   really   intro   run   initial   细节   时间段   cli   类型   

最近我一直在阅读“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 - Resources

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 Verbs

在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 - Hypermedia Controls 

最后一级介绍了您经常听到的HATEOAS(超文本作为应用程序状态引擎的)的丑陋首字母缩写的内容。 它解决了如何从列表中打开广告位以了解如何预约预约的问题。

技术分享

图5: Level 3超媒体控制

我们从我们在第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会再次复制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 - 他们需要弄清楚一个是GET,另一个是DELETE。但是至少它给出了关于在更多信息中考虑什么以及在协议文档中寻找类似的URI的起点。

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

关于如何表示超媒体控件没有绝对的标准。我在这里做的是使用REST实践团队中的当前建议,这是跟随ATOM(RFC 4287),我使用一个<link>元素与目标URI的uri属性和一个rel属性来描述那种关系。一个众所周知的关系(例如对于引用元素本身的self)是裸露的,任何特定于该服务器的关系是完全限定的URI。 ATOM指出,知名链接的定义是链接关系注册表。当我写这些仅限于ATOM所做的工作时,通常被认为是3级休息的领导者。

The Meaning of the Levels

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

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

谈到这一点与伊恩罗宾逊,他强调,当伦纳德理查森首先提出它是与普通设计技术的关系,他发现有吸引力的这个模型。

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

结果是一个模型,可以帮助我们考虑我们想要提供的HTTP服务的类型,并框架希望与之进行交互的人们的期望。

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

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

 

[翻译]Restful Web服务模型

标签:fit   really   intro   run   initial   细节   时间段   cli   类型   

原文地址:http://www.cnblogs.com/birdstudio/p/6687964.html

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!