ditunes / blog

write my idea & share my tunes
1 stars 1 forks source link

Head In First Restful #4

Open ditunes opened 8 years ago

ditunes commented 8 years ago

什么是REST

历史起源

RESTful首次出现在2000年Roy Fielding ((born 1965) is an American computer scientist, one of the principal authors of the HTTP specification, an authority on computer network architecture, and co-founder of the Apache HTTP Server project.)的博士论文中,Roy Fielding是 HTTP 规范的主要编写者之一。 他在架构风格与基于网络的软件架构设计(原版请点击此处)提到:“我这篇文章的写作目的,就是想在符合架构原理的前提下,理解和评估以网络为基础的应用软件的架构设计,得到一个功能强、性能好、适宜通信的架构。作者介绍了表述性状态转移(Representational State Transfer,REST)的架构风格,描述了如何使用REST 来指导现代Web 架构的设计和开发。并使用超文本转移协议(HTTP)和统一资源标识符(URI)的标准阐释REST架构的应用。所以REST只是架构风格,http协议是架构的一种应用

REST定义

Representation state transfer 即表述层状态转移 通俗的来说就是资源(用户基本信息)在网络中以某种表现形式(html json or pdf)进行状态转移(post -> 创建,get->获取 ·····)REST指的是一组架构约束条件和原则。” 如果一个架构符合REST的约束条件和原则,我们就称它为RESTful架构。所以一定要记住REST 是一种架构风格,而不是一种特定技术、规范。简单而言,它由如下因素构成

  1. 直观简短的资源地址:URI,比如:http://example.com/resources/;每一个URI代表一种资源
  2. 资源的表述:Web服务接受与返回的互联网媒体类型,比如:JSON,XML,YAML等。
  3. 对资源的操作:Web服务在该资源上所支持的一系列请求方法(比如:POST,GET,PUT或DELETE)。

REST 名词解释:

资源

任何足够重要并被引用的事物称为资源。也就是说资源可是任何事物,唯一的限制是必须要有一个URI表示它。(什么是URI、URN、URL?). 良好的资源表示方式应遵循可寻址性原则,具有自描述性,需要在形式上给人以直觉上的关联.过去我们会按如下方式表示资源:

http://ddiao.me/abjhdksdjkauujkmkm.json

这种方式,实际上就违背了直觉上关联的原则。我们并不能很直观清楚它对应哪种业务资源。 例如国内比较出名的开源中国社区,它上面的新闻地址就采用这种风格, 如http://www.oschina.net/news/38119/oschina-translate-reward-plan。我们就很清楚这个地址指向一个新闻资源。 例如上述/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08就表示了一个多级的资源, 指的是git用户的git项目的某次提交记录,又例如/orders/2012/10可以用来表示2012年10月的订单记录。

表述

即REST中的Representation。在我看来,表述是资源的某种表现形式,或者是某种状态。如用户个人信息,可以表现为一段json格式数据({username:ddiao,age:10}),也可以是一段文本数据“ddiao 10 years old ”,甚至一个pdf文档。

我们使用浏览器请求服务器获取资源的本质就是获取资源的当前状态(如json格式表述的用户信息资源)。而我们浏览器请求服务器修改资源的行为本质就是变更资源的状态(如增加 x-www-form-urlencoded格式表述 message=test&age=10资源 )。

在HTTP协议中客户端通过ACCEPT头请求特定格式的表述,而服务端通过Content-type告诉客户端资源的表现形式,以github为例: image

统一协议语义

一个REST系统由各个独立组建构成:服务器、客户端、缓存、代理、缓存代理。这些组件相互通信或对资源进行操作是需要遵循一套统一的语义。在HTTP中这种操作语义称为动词(verb)。

什么是安全和幂等性操作?
方法 描述 是否幂等 是否安全
GET 获取某个资源的表述 成功响应:200
POST 基于给定的表述信息,在当前资源的下一级创建新资源 成功响应:201
PUT 用给定的表述信息替换资源的当前状态(更新全部信息),PATCH表示更新部分信息 成功响应:200
DELETE 销毁一个资源 成功响应:200

表述状态转移

http协议是无状态协议,那么客户端和服务端各自维持着自己的状态,他们互相不知道。对于客户端而言,其向服务端发送操作请求(请求一个页面或数据)得到服务端发来的表述(http响应的信息)后发生自身的应用状态(所处的页面或客户端所显示的数据)的改变,而服务端在接收到客户端提交的资源表述后,会导致其资源状态发生变更。这种状态的变化是客户端与服务器之间建立在表现层之上的,所以就是"表现层状态转移"


REST 之错误认识

http不是传输协议

HTTP(HyperText Transfer Protocol) 被翻译为超文本传输协议,然而基于Roy Fielding的论文我们应该将其理解为超文本转移协议.因为该协议表述的是通过服务端与客户端之间进行资源的表述转移实现对资源的操作的web架构语义。它更应该是一种应用协议,而不是传输协议。

错误的使用http status 和 http method

对http协议的错误理解,也导致了我们在使用它的时候发生很多的错误:

  1. method:get 结合url传参方式 修改资源 如 http://xxx.com?method=create&msg=2222;请求创建资源
  2. 服务端无论客户端的操作资源的请求是否执行成功,一律返回Status 200
  3. 客户端不根据http status状态码去判断业务是否执行成功,而是自定义一套error status or error code 来判断(这和第二点的错误紧密相关)

    URI设计存在的问题

  4. 在uri中包含动词,以此来表示对资源操作行为,如 http://xxx.com/getMsg 表示获取信息,然而事实上uri用以标识资源是一种静态的表述,而语义动词则由POST、GET等来表示。
  5. 在uri中包含版本号  如 http://www.example.com/app/2.0/foo 事实上版本号是api文档资源的一种表述。比如我们需要一个最新版本的API文档,那么这个资源表述是抽象的,有多种可能。假设当前版本为2.0则最新版本的API文档为2.0文档。所以说版本号是资源的一种表述

REST 进阶设计

1457531732081

第零级: Swamp of POX

对于level0而言,它只是将http看作一种传输协议来使用,它只是应用了http 发送请求和获取响应这一规则。使用单一的URL和单一的http 方法 如post 。在xml-rpc应用中可以清楚看到它通过发送一种约定的好的xml格式发送服务端,以调用服务端对象的接口。服务端将调用接口以xml方式返回给客户端。这样他们只是使用单一url,单纯依赖http 请求响应通信协议(作为传输协议),结合自己定义数据规则(xml 定义方法、参数的规则)的方式进行交互。

第一级:在架构中引入资源(Resource)的概念。

不同的URI只是作为不同的调用入口,与此同时只使用同一个HTTP方法传输数据。最常见的错误就是在URI中包含动词,比如URI http://example.com/getOrder?orderId=1234,其实「资源」表示一种实体,所以应该是名词,动词应该放在HTTP协议中。而与此同时URI也有可能破坏HTTP GET的安全性和幕等性,比如某个客户端在http://example.com/updateOrder?id=1234&coffee=latte上执行GET(而不是POST),就能创建一笔新的咖啡订单(一个资源),按理来说GET请求不能改变服务的任何状态。

第二级:每一个URI代表一种资源,支持HTTP动词。

此时使用多个URI的话,需要让不同的URI代表不同的资源(注意多个URI可能指向同一个Resource,而一个URI不能指向不同Resource。),同时使用多个HTTP方法操作这些资源,例如使用POST/GET/PUT/DELET分别进行CRUD操作。这时候HTTP头和有效载荷都包含业务逻辑,例如HTTP方法对应CRUD操作,HTTP状态码对应操作结果的状态。我们现在看到的大多数所谓RESTful API做到的也就是这个级别。《REST实战》的译者也谈到:悟性差的人,理解到CRUD式Web服务就满足了。而悟性好的人,可以彻底理解超文本驱动,甚至是与REST关系密切的语义网,最终达到 REST开发的最高境界。

第三级:HATEOAS(Hypermedia As The Engine Of Application State ),使用超媒体(hypermedia)作为应用状态引擎。

根据Roy的严格规定,超媒体(hypermedia)是REST的先决条件。任何其他东西不应该自我标榜为REST。要解释HATEOAS这个概念先要解释什么是超媒体:我们已经知道什么是多媒体(multimedia),以及什么是超文本(hypertext)。其中超文本特有的优势是拥有超链接(hyperlink)。如果我们把超链接引入到多媒体当中去,那就得到了超媒体,因此关键角色还是超链接。使用超媒体作为应用引擎状态,意思是应用引擎的状态变更由客户端访问不同的超媒体资源驱动。 让我们来看个实例,这个响应内容可能略有不同: GET https://api.example.com/profile

        {
          "name": "Steve",
          "picture": {
            "large": "https://somecdn.com/pictures/1200x1200.png",
            "medium": "https://somecdn.com/pictures/100x100.png",
            "small": "https://somecdn.com/pictures/10x10.png"
          }
        }

由于在响应中包含了链接地址,因此使用该API的客户端就能够自由选择要下载怎样的信息。这些链接告知了客户端有哪些选择,并且它们的地址在哪里。因此在这里我们无需同时返回三个不同版本的用户档案图片,我们所做的只是告诉客户端有三种可用的图片尺寸可以选择,并且告诉客户端能够在哪里找到这些图片。这样一来,客户端就能够根据不同的场景,做出符合自身需要的选择。而且,如果客户端只需要一种格式的图片,那就无需下载全部三种版本的图片了。这样一来可谓一箭三雕:既减少了网络负载,又增进了客户端的灵活性,更增进了API的可探索性。 超媒体的核心概念就是所谓的元素,而这些相互链接的资源实际上描述了一个协议,即引导我们达成某个目标的一系列步骤,例如订购一杯咖啡所需要的点单、付款、取咖啡等等。这就是超媒体的本质:经由资源之间的链接,我们改变整个应用的状态,即超媒体转换了分布式应用的状态。需要注意的是,服务器和消费者两者间交换的是资源状态的表述,而不是应用的状态,被转移的表述中包括了反应应用状态的链接。在这里也推荐直接看看设计非常优秀的GitHub API,以便于更好地理解真实的RESTful API以及hypermedia的概念。


超媒体解决的问题

当你得到一个url的时候,客户端如何知道这个可以对这个url发起哪些请求(get or post and ....) ?这个url对应的何种资源的表述?·······过去当我们要接入外部系统的时候,我们需要读取大量的api文档去获取这方面的知识,编写不同风格的api,那么有没有一种自动化的方式,让客户端自动的推进。超媒体就是要将资源以客户端可读的方式连接起来,即客户端可以通过超媒体信息去自动根据情形去判断操作什么及怎么操作资源。它是一种服务端与客户端通话的机制。就类似服务端给予客户端一个菜单,客户端可以根据自己需求去选择发生什么(改变当前应用状态)


扩展阅读

terrilltang commented 7 years ago

很不错,挺详细,比我百度到的一些资料好,但我还处于后端语言学习阶段,直接走这样的架构,没有相应的简单的案例可以拿来学习。不过通篇看下来,理解了什么是REST,但后面带的那个ful是什么意思。

ditunes commented 7 years ago

@terrilltang ful应该是形容词尾缀,使的rest变成形容词。以用来形容那些遵循rest规则的系统。至于案例可以从api入手啊 了解http语义 一些关于url基本规则要求,看看github api 至少很容易就可以达到Level 2级,还能对3级进化有思路 。至于roy fielding的论文说实在我还要花时间领会,现在也只是有一部分内容有些似懂非懂感觉,这部分先不碰