REST

基于HTTP的REST系统的整个运行流程:以获取电商页面商品分类为例(https://www.cnblogs.com/loveis715/p/4669091.html  ),在开始的时候,我们拿到了所有分类的列表。列表中的各个条目不仅仅包含了用户可以看到的分类名称等信息,更拥有一个额外的URL属性。在用户选择该列表中的一项时,页面逻辑将会向对应的URL发送一个请求,以获得该项目的详细信息。在这个详细信息中,一些内容又包含了一些其它的URL,从而使得页面逻辑又能通过该URL属性发送请求。

REST的定义

OK,现在让我们来看看REST的定义。Wikipedia是这样描述它的:

Representational State Transfer (REST) is a software architecture style consisting of guidelines and best practices for creating scalable web services. REST is a coordinated set of constraints applied to the design of components in a distributed hypermedia system that can lead to a more performant and maintainable architecture.

  从上面的定义中,我们可以发现REST其实是一种组织Web服务的架构,而并不是我们想象的那样是实现Web服务的一种新的技术,更没有要求一定要使用HTTP。其目标是为了创建具有良好扩展性的分布式系统。

反过来,作为一种架构,其提出了一系列架构级约束。这些约束有:

1.使用客户/服务器模型。客户和服务器之间通过一个统一的接口来互相通讯。

2.层次化的系统。在一个REST系统中,客户端并不会固定地与一个服务器打交道。

3.无状态。在一个REST系统中,服务端并不会保存有关客户的任何状态。也就是说,客户端自身负责用户状态的维持,并在每次发送请求时都需要提供足够的信息。

所谓无状态的,即所有的资源,都可以通过URI定位,而且这个定位与其他资源无关,也不会因为其他资源的变化而改变。有状态和无状态的区别,举个简单的例子说明一下。如查询员工的工资,如果查询工资是需要登录系统,进入查询工资的页面,执行相关操作后,获取工资的多少,则这种情况是有状态的,因为查询工资的每一步操作都依赖于前一步操作,只要前置操作不成功,后续操作就无法执行;如果输入一个url即可得到指定员工的工资,则这种情况是无状态的,因为获取工资不依赖于其他资源或状态,且这种情况下,员工工资是一个资源,由一个url与之对应,可以通过HTTP中的GET方法得到资源,这是典型的RESTful风格。

这里写图片描述 

这里写图片描述

 

4.可缓存。REST系统需要能够恰当地缓存请求,以尽量减少服务端和客户端之间的信息传输,以提高性能。

5.统一的接口。一个REST系统需要使用一个统一的接口来完成子系统之间以及服务与用户之间的交互。这使得REST系统中的各个子系统可以独自完成演化。

  如果一个系统满足了上面所列出的五条约束,那么该系统就被称为是RESTful的。

  下面再次通过电子商务网站egoods这个示例来帮助我们理解这些约束。首先,egoods是一个电子商务网站。用户需要通过浏览器,手机或者网站所发布的浏览应用来访问该网站的内容。因此其使用的自然是客户/服务器模型。而在浏览过程中,用户需要访问不同类型的数据,如商品描述、购物车等信息。这些信息可能由egoods网站服务中不同的服务器来提供的,因此在用户浏览过程中可能需要与不止一个服务器进行交互。如果在服务端保存了有关客户的任何状态,那么在用户与不同服务器进行交互的时候,客户的状态就需要在这些服务之间进行同步,大大地增加了系统的复杂度。因此,REST要求客户端自行维护状态,并在每次发送请求的时候提供自身所储存的处理该请求所必需的信息。而恰当地使用缓存这一条也非常容易理解。在客户端请求一个自上次请求后没有发生过变化的信息时,如产品分类列表,服务端仅仅需要返回一个304响应即可。

  这里可以看到,前四条约束中除了无状态这条约束较为特别之外,其它三条约束在基于HTTP的Web服务中都很常见,也较容易达成。而无状态约束在其它类型的Web服务中并不十分常见,因此如何避免违反该约束是在实现REST服务时最常讨论的话题。其不仅仅会影响到很多功能的设计,更是REST系统扩展性的关键。因此在后面的章节中,我们会对无状态约束单独进行讲解。

  在简单地介绍了前四个约束之后,我们就需要着重讲解统一接口这个约束了。可以说,前面的四个约束实际上都较为容易达成。唯一需要注意的无非是是否某些技术实现违反了这些约束。而第五条约束,统一接口,可以说是REST服务设计的核心所在,也是决定REST服务设计的成败之处。在实现一个基于HTTP的REST服务时,软件开发人员不仅仅需要考虑REST所设置的一系列约束,更需要考虑HTTP各组成的语意,HTTP相关技术如何与REST服务约束结合,如何保持前后向兼容性以及如何进行版本管理等问题,才能给出一个自然的,具有较高易用性和较强生命力的REST系统。

  而在介绍统一接口约束之前,我们则需要了解一下和REST密切相关的两个名词:资源和状态。可以说,资源是REST系统的核心概念。所有的设计都会以资源为中心,包括如何对资源进行添加,更新,查找以及修改等。而资源本身则拥有一系列状态。在每次对资源进行添加 ,删除或修改的时候,资源就将从一个状态转移到另外一个状态。

  比如说,在egoods中,商品的分类就是一种资源。该资源有很多实例,包括表示食品的分类,其所对应的URL是“/api/categories/1”。同样地,食品的品牌也是一种资源。这些资源的实例都对应着一个当前的状态。在修改了一个资源实例之后,比如修改了食品分类中的热搜关键字,那么其将对应着一个新的状态。这种状态之间的变化被称为是状态的转移。

REST 是面向资源的,这个概念非常重要,而资源是通过 URI 进行暴露。 
URI 的设计只要负责把资源通过合理方式暴露出来就可以了。对资源的操作与它无关,操作是通过 HTTP动词来体现,所以REST 通过 URI 暴露资源时,会强调不要在 URI 中出现动词。

REST很好地利用了HTTP本身就有的一些特征,如HTTP动词、HTTP状态码、HTTP报头等等 
REST API 是基于 HTTP的,所以你的API应该去使用 HTTP的一些标准。这样所有的HTTP客户端(如浏览器)才能够直接理解你的API(当然还有其他好处,如利于缓存等等)。REST 实际上也非常强调应该利用好 HTTP本来就有的特征,而不是只把 HTTP当成一个传输层这么简单了。

在大概了解了REST系统中的资源和状态的定义后,我们来看看统一接口这个约束。该约束又包含了四个子约束:

1.每个资源都拥有一个资源标识。每个资源的资源标识可以用来唯一地标明该资源。

所有的Url本质来讲,都应该是一种资源。一个独立的Url地址,就是对应一个独一无二的资源。一个冰淇淋,一个老师,一间房子,在Url上对应的都是一个资源,不会有多余的Url跟他对应,也不会表示有多个Url地址  
  注意,这里点的是Url地址,并不是单独的参数,他就是一个/room/{room_id}这样的东西,举个栗子,/room/3242 这就表示3242号房间。这是一个清爽的世界啊,你想想,之前的Url是什么都要,我开房,可能是/open/room/3242 我要退房可能是/exit/3242/room,我要打理房间,可能是room/3242?method=clean.够了!这些乱七八糟的东西全够了,让世界回归清爽的本质,一间房,就是/room/3242 没有别的Url地址了。 
  在过去的混乱世界里,经常用的就是Get和Post。如果不是因为Get不支持大数据传输,我想连Post都不会有人使用。

  而对资源最常见的操作是什么?CRUD,对不对,就是创建,读,更新,删除。再看Http的Method?是不是非常完美?其实也怪Fielding老爷子一开始命名不准确,如果刚开始就是把Get方法叫做ReadPut方法叫做UpdatePost叫做Create这该多好。。。 

2.消息的自描述性。在REST系统中所传递的消息需要能够提供自身如何被处理的足够信息。例如该消息所使用的MIME类型,是否可以被缓存等。

3.资源的自描述性。一个REST系统所返回的资源需要能够描述自身,并提供足够的用于操作该资源的信息,如如何对资源进行添加,删除以及修改等操作。也就是说,一个典型的REST服务不需要额外的文档对如何操作资源进行说明。

4.HATEOAS。即客户只可以通过服务端所返回各结果中所包含的信息来得到下一步操作所需要的信息,如到底是向哪个URL发送请求等。也就是说,一个典型的REST服务不需要额外的文档标示通过哪些URL访问特定类型的资源,而是通过服务端返回的响应来标示到底能在该资源上执行什么样的操作。一个REST服务的客户端也不需要知道任何有关哪里有什么样的资源这种信息。

现在,让我们仍然以egoods作为示例来解释一下上面四个子约束。

  在前面的章节中,我们已经看到了从egoods所返回的表示食品这个分类的响应:

1 HTTP/1.1 200 OK
 2 Content-Type: application/json
 3 Content-Length: xxx
 4 
 5 {
 6    "url" : "/api/categories/1",
 7    "label" : "Food",
 8    "items_url" : "/api/items?category=1",
 9    "brands" : [
10          {
11             "label" : "友臣",
12             "brand_key" : "32073",
13             "url" : "/api/brands/32073"
14          }, {
15             "label" : "乐事",
16             "brand_key" : "56632",
17             "url" : "/api/brands/56632"
18          }
19          ...
20    ],
21    "hot_searches" : …
22 }

格式固定,第一行永远是操作失败或者成功的状态码,第二行是返回的类型,第三行内容的长度,第五行开始是内容。

这样我只需写一个程序解析返回的信息就可以了,可以重用,但是我们上面传统的不仅仅要协商,还有有不同的解析程序,稍微改变,就不能正常使用了。所以rest的明显更加通用。

       首先我们看到的是,该响应通过Content-Type响应头来标示响应中所包含的信息是按照JSON格式来组织的。在看到了该响应头中所标示的格式之后,消息的接收方就可以按照JSON的格式理解或分析该响应中的负载。这也便是消息的自描述性。

  当然,消息的自描述性不仅仅包含如何解析其所携带的负载。在一个基于HTTP的REST系统中,我们可以通过使用大部分HTTP标准所提供的功能来提高消息的自描述性。由于这些功能已经拥有了完备的文档,被广大的软件开发人员所熟知,并得到了众多浏览器厂商以及Web类库的支持,因此根据这些标准实现REST服务具有较高的消息自描述性。举例来说,如果在请求中标明了If-Modified-Since头,那么服务端将可能返回一个304 Not Modified响应。在看到该响应的时候,浏览器或其它浏览工具可以从缓存中取得上一次得到的结果。因此,在一个基于HTTP的REST系统中,如何准确地使用HTTP协议是一项非常重要的内容。

  在获知了如何对响应所携带的负载进行解析之后,我们就来看看资源的自描述性。在上面的示例中,服务端响应使用了JSON表示了食品分类。该表示首先通过label属性描述了自己是一个什么分类。接下来,其通过brands属性表示了该分类中的著名品牌,并通过hot_searches标示了在该分类中的热搜关键字。可以看到,该负载中的所有属性都清晰地描述了自身所表达的含义。

  那在该资源表示中的url属性是什么意思?实际上这是为子约束“每个资源都拥有一个资源标识”所添加的一个属性。该子约束要求每个资源的资源标识可以用来唯一地标明该资源。对于网络应用来说,资源标识就是URI。而在一个基于HTTP的系统中,最自然的资源标示便是URL。在表示单个资源的时候,这个URL常常会包含着资源在该类资源中的ID。

  在本文的其它章节中,我们就将以这种方式来区分URL和ID:URL用来指向资源所在的地址,而ID则表示该资源在该类型资源中的ID。请读者一定要记得这两个术语所对应的不同意义,以防止理解错误。

  现在还有一部分食品分类表示中的属性没有被讲解,那就是在该表示中的各个URL。这是为子约束HATEOAS服务的。在用户看到items_url属性时,其就可以通过向该URL发送GET消息得到属于食品分类中的所有商品的列表。而在商品品牌的表示中也拥有一个url属性。也就是说,向该URL发送一个GET请求也能够得到相应品牌的详细信息。

  您可能会问:既然在介绍HATEOAS时说REST服务并不需要文档来告诉用户哪里拥有什么样的资源,那用户应该如何知道向/api/categories发送GET请求就能得到所有的分类呢?标准的做法则是向/api直接发送一个GET请求:

1 GET /api
2 Host: www.egoods.com
3 Authorization: Basic xxxxxxxxxxxxxxxxxxx
4 Accept: application/json

  而在返回的响应中将标示出REST API的版本以及所有可以访问的资源等信息:

 1 HTTP/1.1 200 OK
 2 Content-Type: application/json
 3 Content-Length: xxx
 4 
 5 {
 6    "version": "1.0",
 7    "resources": [
 8       {
 9          "label" : "Categories",
10          "description" : "Product categories",
11          "uri": "/api/categories"
12       }, {
13          "label" : "Items",
14          "description" : "All items on sell",
15          "uri": "/api/items"
16       }
17    ]
18 }

  可以看到,在该响应中列出了可以被访问的两种资源:表示商品分类的Categories以及表示商品的Items。在需要访问特定类型的资源时,软件开发人员可以通过直接向这两种资源所对应的URI发送GET请求即可。

 

参考:

https://www.cnblogs.com/loveis715/p/4669091.html

https://blog.youkuaiyun.com/qq_21383435/article/details/80032375

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值