REST架构(Representational State Transfer (对资源的)表述性状态转移)是什么?(面向资源)RESTfulURI设计技巧幂等性 安全性无状态
Posted Dontla
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了REST架构(Representational State Transfer (对资源的)表述性状态转移)是什么?(面向资源)RESTfulURI设计技巧幂等性 安全性无状态相关的知识,希望对你有一定的参考价值。
文章目录
REST
为什么会有REST呢?
Web服务已经成为异构系统之间的互联于集成的主要手段,过去Web服务几乎都是采用SOAP(Simple Object Access Protocol,简单对象访问协议)来构建的。REST风格的软件架构模式出现后就快速取代了复杂而笨重的SOAP,成为Web API的标准。
什么是REST呢?
REST全称Representational State Transfer表述性状态转移
REST于2000年被Roy Thomas Fielding博士提出,是基于HTTP、URI、XML、JSON等标准和协议,支持轻量级、跨平台、跨语言的架构设计,是Web服务的一种新的架构风格。
Roy Thomas Fielding将REST定位为“分布式超媒体应用(Distributed Hypermedia System)”的架构风格。
例如:在面向终端用户的Web应用中,应用状态表示Web应用中客户端状态,简单理解可视为会话状态。资源在浏览器中以超媒体的形式呈现,通过点击超媒体中的超链接获取其它相关的资源或对当前资源进行相关处理。另外,获取的资源或针对资源处理的响应同样以超媒体的形式再次呈现在浏览器上。因此,超媒体成为了驱动客户端会话状态转换的引擎(Hypermedia as the engine of appliation state, HATEOAS)。
借助于超媒体这种特殊的资源呈现方式,应用状态的转换体现为浏览器中呈现资源的转换
。如果将超媒体进一步抽象成一般意义上的资源呈现(Representation)方式,那么应用状态变成了“可被呈现的状态(Representational State)”。因此,应用状态之间的转换就成了“可被呈现的状态转换(Representational State Transfer)”,也就是REST。
REST描述了一种架构样式的网络系统,如Web应用程序。在目前主流的Web服务交互方案中,REST相比SOAP(Simple Object Access Protocol,简单对象访问协议)和XML-RPC更加简单明了,无论是对URL的处理还是对Payload的编码,REST都倾向于使用更加简单轻量的设计和实现。
REST其实是对资源的表述性状态转移,它并没有明确的标准而更像是一种设计风格。
- 表述性(Representational)是指客户端请求一个资源,服务器拿到的这个资源,就是表述。
- 资源是REST系统的核心概念,所有的设计都是以资源为中心的。
- 资源的地址在Web中就是URL统一资源定位符
REST架构原则
- 对网络上所有资源都有一个资源标识符
- 对资源的操作不会改变标识符
- 同一资源有多种表现形式,如XML、JSON…
- 所有操作都是无状态的(Stateless)(无状态是什么意思?见下面👇:客户端单次请求必须包含服务器处理该请求所需的所有信息)
REST系统特征
- 客户端与服务器(Client-Server)
提供服务的服务器和使用服务的客户端需要被隔离对待 - 无状态(Stateless)
来自客户端的每个请求必须包含服务器处理该请求所需的所有信息,换句话说,服务器不能存储来自某个客户端某个请求中的信息,并在该客户端的其它请求中使用。 - 可缓存(Cachable)
服务器必须让客户端知道请求是否可以被缓存(什么意思,怎么缓存?) - 分层系统(Layered System)
服务器和客户端之间的通信必须被标准化,即允许服务器和客户端之间的中间层可以代替服务器对客户端的请求进行回应,而且这些对客户端来说不需要特别支持。(哪些中间层,怎么回应?) - 统一接口(Uniform Interface)
客户端和服务器之间通信的方法必须是统一化的 - 支持按需代码(Code-On-Demand)
服务器可以提供一些代码或脚本并在客户端的运行环境中执行,这条准则是唯一不必必须满足的。
REST成熟度的层次
- 第一个层次Level0的Web服务
使用HTTP作为传输方式,实际只是远程方法调用RPC的一种具体形式,如SOAP和XML-RPC。 - 第二个层次Level1的Web服务
引入资源的概念,每个资源都有与之对应的标识符和表达。 - 第三个层次Level2的Web服务
使用不同的HTTP方法来进行不同的操作,并使用HTTP状态码表示不同结果。 - 第四个层次Level3的Web服务
使用HATEOAS,在资源的表达中包含了链接信息,客户端可以根据链接来发现可以执行的动作。
简单来说,REST就是使用URL定位资源 + 使用HTTP动词(GET/POST/DELETE/HEAD/PUT)描述操作。
RESTful
RESTful是一种常见的REST应用,是遵循REST风格的Web服务,而REST风格的Web本质则是一种面向资源的架构(Resource Oriented Architecture, ROA)。
如果一个架构符合REST原则就称为RESTful架构,HTTP就是RESTful架构风格的一个典型应用。
资源(Resources)
表述性状态转移中的表述其实指的是资源(Resources),任何事物只要有被引用到的必要,它就是一个资源。资源可以是实体也可以是一种抽象概念,要让资源可以被识别,就需要具有唯一标识。
资源是一个很宽泛的概念,任何寄宿于Web可供操纵的“事物”都可视为资源,资源可以体现为经过持久化处理保存到磁盘上的某个文件或是数据库中某个表的记录,也可以是Web应用接收请求后采用某算法计算所得的结果。简单来说,资源可以体现为一个具体的物理对象,也可以是一个抽象的流程。
资源是一种信息实体,可以有多种外在表现形式,资源具体呈现出来的形式称为表现层(Representation)。例如:文本可通过txt格式表现也可用html表现等。
统一资源标识符 URI(Uniform Resource Identifier)(一般说URI指的就是URL,URN可忽略)
一个资源必须具有一个或多个标识,在设计Web应用的API时,由于Web系统中资源的唯一标识是URI(Uniform Resource Identifier,统一资源标识符),所以自然采用URI作为资源的标识。
schema://host[:port]/path[?query-string][#anchor]
schema 指定底层所使用的协议,如HTTP、HTTPS、FTP...
host 服务器的IP地址或域名
port 服务器端口,默认80
path 访问资源的路径
query-string 发送给HTTP服务器的数据
anchor 锚点
关于上面最后一个锚点,可参考:URL中的锚点(fragment、片段标识符)是什么?(hash mark(#))(HTML 页面内定位)(之前学html不是学了吗?忘啦?)(SEO 搜索引擎优化)
URI既可以看作是资源的地址,也可以看作是资源的名称。如果某些信息没有使用URI来表示,那它就不能算是一个资源,只能算是资源的信息。
URI的设计遵循着可寻址性原则,具有自描述性,在形式上需要给人直觉上的关联。因为具有可读性的URI更容易被使用,使用者一看就知道被标识的是何种资源。
除了必要的标志性和可选的可读性之外,标识资源的URI应该具有“可寻址性
(Addressability)”,也就是说,URI不仅仅指明了被标识资源所在的位置,通过这个URI还可以直接获取目标资源。
URI具有URL和URN两种主要的表现形式,由于URL具有可寻址性,所以推荐采用URL作为资源的标识。
URI除了可以标识某个独立的资源外,还可以标识一组资源的集合或者是资源的容器。当然,一组同类资源的集合或存放一组同类资源的容器本身也可以视为另一种类型的复合型(Composite)资源。所以,“URI总是标识某个资源”的说法是没错的。
REST是面向资源的,而资源是通过URI进行暴露,URI的设计只负责把资源通过合理的方式暴露出来就可以了。对资源的操作与它无关,操作是通过HTTP动词来体现的。所以REST通过URI暴露资源时,会强调不要在URI中出现动词
。
URI只代表资源的实体,并不代表它的形式。严格来说,网址后的文件后缀是不必要的,因为文件后缀标识的是格式,是属于表现层的范畴。URI应该只代表资源的位置,它的具体表现形式,应该在HTTP请求的头信息中使用Accept和Content-Type字段指定,因为这两个字段才是对表现层的描述。(Accept是什么?)
例如:github站点的URI的设计https://github.com/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08
URI设计技巧
-
使用下划线_或中划线-分隔单词可以让URI可读性更好
例如:http://www.oschina.net/news/38119/oschina-translate-reward-plan
-
使用斜线/表示资源的层级关系
例如:/git/git/commit/e3af72cdafab5993d18fae056f87e1d675913d08
表示一个多级的资源,表示git用户的git项目的某次提交
例如:/orders/2019/10
表示2019年10月的订单记录 -
使用问号
?
用于过滤资源
URI中的?
并非只能用于传递参数,?
可以用于对资源的过滤。(其实就是程序中对查找字符串的判断嘛)
例如:/git/git/pulls?state=closed
表示git项目中已经关闭的推入请求 -
使用逗号,或分号;用来表示同级资源的关系
为了表示同级资源的关系,可使用逗号,
或分号;
进行分隔
例如:/git/git /block-sha1/sha1.h/compare/e3af72cdafab5993d18fae056f87e1d675913d08;bd63e61bdf38e872d5215c07b264dcc16e4febca
比较git中某个文件两次提交记录之间的差,现在GitHub使用...
三个点号来比较两次提交记录之间的差异。
统一资源接口
REST是指一组架构约束条件和原则,满足这些约束条件和原则的应用程序或设计就是RESTful。
RESTful架构的核心规范与约束是统一接口,又分为四个子约束:
- 每个资源都拥有一个资源标识,每个资源的资源标识可以用来唯一的标明该资源。
- 消息的自描述性
- 资源的自描述性
- 使用超媒体作为应用状态引擎
Web应用程序中最重要的RESTful原则是:客户端和服务器之间的交互在请求之间是无状态的。
从客户端到服务器的每个请求都必须包含理解请求所必须的信息。如果服务器在请求之间的任何时间点重启,客户端将不会得到通知。另外,无状态请求可以由任何可用服务器应答,这一点十分适合云计算之类的环境,客户端可以缓存数据以改进性能。
在服务器上应用程序状态和功能可以划分为各种资源,资源是一个有趣的概念实体,它向客户端公开。每个资源都使用URI得到一个唯一的地址。所有资源都共享统一的接口,以便在客户端和服务器之间传递状态。
RESTful架构应该遵循统一接口原则,统一接口包括一组受限且预定义的操作,无论是什么样的资源,都是通过使用相同的接口进行资源的访问。接口使用标准的HTTP方法如GET、POST、PUT等,并遵循这些方法的语义。
如果按照HTTP方法的语义来暴露资源,那么接口将会拥有安全性和幂等性的特性。例如:GET和HEAD请求都是安全的,无论请求多少次,都不会改变服务器的状态。而GET、HEAD、PUT、DELETE请求又都是幂等的,无论对资源操作多少次,结构总是一样的,后续的请求并不会产生比第一次更多的影响。
关于HTTP请求方法具有两个基本的特性,即“安全性”和“幂等性”。
- 幂等性(Idempotent):表示对同一REST接口的多次访问得到的资源状态都是相同的
- 安全性:表示对REST接口访问将不会使服务器资源的状态发生改变
状态转化
实际上状态分为应用状态
和资源状态
,客户端负责维护应用状态,服务器维护资源状态
。客户端与服务器的交互必须是无状态的,在每次请求中包含处理该请求所需的一切信息。服务器不需要在请求之间保留应用状态,只有在接收到实际请求时服务器才会关注应用状态。这种无状态的通信原则,使得服务器和中介能够立即独立的请求和响应。在多次请求中,同一客户端也不再需要依赖于同一台服务器,方便实现高可扩展和高可用性的服务器。简单来说,这种无状态的通信原则并不是说客户端应用不能有状态,而是指服务器不应该保存客户端的状态。
会话状态不是作为资源状态保存在服务器的,而是被客户端作为应用状态进行跟踪的。客户端应用状态在服务器提供的超媒体的指引下发生变迁。服务器通过超媒体告诉客户端当前状态有那些后续状态可以进入。
客户端与服务器的一个互动过程中势必涉及到数据和状态的变化,由于HTTP本身是无状态的,这意味着所有的状态都保存在服务器上。因此,如果客户端想要操作服务器,必须通过某种手段让服务器发生“状态转化(State Transfer)
”。而这种转化是建立在表现层之上的,所以就是“表现层状态转化
”。
客户端能够用到的手段只能是HTTP协议,具体来说是HTTP协议中四个表示操作方式的动词:GET、POST、PUT、DELETE,它们分别对应四种基本操作:
- GET用来获取资源
- POST 用于新建资源,也可以用于更新资源。
- PUT 用于更新资源
- DELETE 用于删除资源
RESTful对于资源的操作主要包括增删改查CURD,由HTTP动词或谓词表示。
- POST:用于在服务器中新建一个资源,对应资源操作是增加INSERT,非幂等且不安全。
- DELETE:用于从服务器删除资源,对应资源操作是删除DELETE,幂等且不安全。
- PUT:用于在服务器中更新资源,客户端提供改变后的完整资源,对应资源操作是修改UPDATE,幂等且不安全。
- GET:用于从服务器中取出资源,对应资源操作是查询SELECT,幂等且安全。
资源的表述
客户端通过HTTP方法可获取资源,准确来说客户端获取的只是资源的表述而已。资源在外界的具体呈现方式可以有多种表现形式,在客户端和服务器之间传输的也是资源的表述,而不是资源本身
(?不太理解)。例如:文本资源可以采用HTML、XML、JSON等格式,图片可以使用PNG、JPG等格式展现。
资源的表述包括数据和描述数据的元数据,例如:HTTP头信息的Content-Type字段就是这样的一个元数据属性。
问题是客户端如何知道服务器提供那种表述形式呢?答案是通过HTTP内容协商,客户端可以通过Accept头请求一种特定格式的表述,服务器则通过Content-Type告知客户端资源的表述形式
(不太理解?)。
例如:请求组织资源的JSON格式的表述形式
# HTTP请求头
GET http://api.github.com/orgs/github HTTP/1.1
Accept: application/json
# HTTP响应头
HTTP/1.1 200 OK
Content-Type: application/json; charset=utf-8
最佳实践
在URI中带上版本号
例如http://api.example.com/1.0/foo
,如果把版本号理解成资源的不同表述形式的话,那就应该只是用一个URL,并通过Accept字段来区分,以Github为例其格式为application/vnd.github[.version].param[+json]
。
Accept: vnd.example-com.foo+json; version=1.0
资源的URL设计,可通过URL来表示资源,首先资源可分为主资源和子资源,主资源表示一类独立的资源,索引主资源应该直接放到相对路径下。一个子资源可能是一个集合也可能是一个单一的子资源。
例如:表示主资源的实例/goods/1
,表示子资源的集合/goods/1/pictures
。
RESTful的核心思想是客户端发出的数据操作指令都是“动词+宾语”的结构
比如:GET /users
这个命令,其中GET
是动词,/users
是宾语。
动词通常就是五种HTTP方法对应着CURD操作,根据HTTP规范动词一律大写。有些客户端只能使用GET和POST两种方法,服务器必须接收POST模拟其它三个方法PUT、PATCH、DELETE,这时客户端发出的HTTP请求中就需要附加上X-HTTP-Method-Override
属性,它会覆盖掉POST方法,用以告知服务器应该使用哪一个动词。
例如:下列请求的方法是PUT而非POST
POST /api/users/1 HTTP/1.1
X-HTTP-Method-Override: PUT
- 宾语必须是名词
宾语是API的URL,是HTTP动词作用的对象,它应该是名词,不能是动词。既然URL是名词,那么应该使用复数还是单数呢?这个没有统一的规定,常见的操作是读取一个集合时使用复数。为了统一起见建议都使用复数的URL
。
- 避免多级URL
当资源需要多级分类时就会很容易出现多级URL,比如获取某个用户的某类数据:GET /users/10/categories/2,这种URL不利于扩展而且语义也不明确,往往需要想一下才能明白含义。更好的做法时除了第一级其它级别都使用查询字符串表示
,比如GET /users/10?categories=2
。
比如要查询某个状态的数据时会设计成这样GET /users/locked
,但使用查询字符串的写法明显会更好 GET /users?locked=true
。
REST很好地利用了HTTP本身的一些特性,如HTTP动词、HTTP状态码、HTTP报头等。REST API是基于HTTP的,所以设计的API应该去使用HTTP的一些标准,这样所有的HTTP客户端才能够直接理解你的API并有利于缓存等。RSET实际上也非常鼓励应该利用好HTTP本来就有的特性,而不是只把HTTP当成一个传输层协议这样简单。
以上是关于REST架构(Representational State Transfer (对资源的)表述性状态转移)是什么?(面向资源)RESTfulURI设计技巧幂等性 安全性无状态的主要内容,如果未能解决你的问题,请参考以下文章
REST Representational state transfer