RESTful 架构详解
REST全称是Representational State Transfer,中文意思是表述(编者注:通常译为表征)性状态转移。
2. 理解RESTful
从资源的定义、获取、表述、关联、状态变迁等角度,列举一些关键概念并加以解释。
-
资源与URI
-
统一资源接口
-
资源的表述
-
资源的链接
-
状态的转移
2. 1 资源与URI
- URI既可以看成是资源的地址,也可以看成是资源的名称。如果某些信息没有使用URI来表示,那它就不能算是一个资源, 只能算是资源的一些信息而已。
下面让我们来看看URI设计上的一些技巧:
-
使用_或-来让URI可读性更好 如http://www.oschina.net/news/38119/oschina-translate-reward-plan。
-
使用/来表示资源的层级关系 又例如/orders/2012/10
-
使用?用来过滤资源 而/pulls?state=closed用来表示git项目中已经关闭的推入请求,
-
,或;可以用来表示同级资源的关系
2. 2 统一资源接口
不论什么样的资源,都是通过使用相同的接口进行资源的访问。接口应该使用标准的HTTP方法如GET,PUT和POST,并遵循这些方法的语义。
下面列出了GET,DELETE,PUT和POST的典型用法:
GET
-
安全且幂等
-
获取表示
-
变更时获取表示(缓存)
-
200(OK) - 表示已在响应中发出
-
204(无内容) - 资源有空表示
-
301(Moved Permanently) - 资源的URI已被更新
-
303(See Other) - 其他(如,负载均衡)
-
304(not modified)- 资源未更改(缓存)
-
400 (bad request)- 指代坏请求(如,参数错误)
-
404 (not found)- 资源不存在
-
406 (not acceptable)- 服务端不支持所需表示
-
500 (internal server error)- 通用错误响应
-
503 (Service Unavailable)- 服务端当前无法处理请求
POST
-
不安全且不幂等
-
使用服务端管理的(自动产生)的实例号创建资源
-
创建子资源
-
部分更新资源
-
如果没有被修改,则不过更新资源(乐观锁)
-
200(OK)- 如果现有资源已被更改
-
201(created)- 如果新资源被创建
-
202(accepted)- 已接受处理请求但尚未完成(异步处理)
-
301(Moved Permanently)- 资源的URI被更新
-
303(See Other)- 其他(如,负载均衡)
-
400(bad request)- 指代坏请求
-
404 (not found)- 资源不存在
-
406 (not acceptable)- 服务端不支持所需表示
-
409 (conflict)- 通用冲突
-
412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
-
415 (unsupported media type)- 接受到的表示不受支持
-
500 (internal server error)- 通用错误响应
-
503 (Service Unavailable)- 服务当前无法处理请求
PUT
-
不安全但幂等
-
用客户端管理的实例号创建一个资源
-
通过替换的方式更新资源
-
如果未被修改,则更新资源(乐观锁)
-
200 (OK)- 如果已存在资源被更改
-
201 (created)- 如果新资源被创建
-
301(Moved Permanently)- 资源的URI已更改
-
303 (See Other)- 其他(如,负载均衡)
-
400 (bad request)- 指代坏请求
-
404 (not found)- 资源不存在
-
406 (not acceptable)- 服务端不支持所需表示
-
409 (conflict)- 通用冲突
-
412 (Precondition Failed)- 前置条件失败(如执行条件更新时的冲突)
-
415 (unsupported media type)- 接受到的表示不受支持
-
500 (internal server error)- 通用错误响应
-
503 (Service Unavailable)- 服务当前无法处理请求
DELETE
-
不安全但幂等
-
删除资源
-
200 (OK)- 资源已被删除
-
301 (Moved Permanently)- 资源的URI已更改
-
303 (See Other)- 其他,如负载均衡
-
400 (bad request)- 指代坏请求
-
404 (not found)- 资源不存在
-
409 (conflict)- 通用冲突
-
500 (internal server error)- 通用错误响应
-
503 (Service Unavailable)- 服务端当前无法处理请求
下面我们来看一些实践中常见的问题:
- POST和PUT用于创建资源时有什么区别?
POST和PUT在创建资源的区别在于,所创建的资源的名称(URI)是否由客户端决定。
生成的路径就是分类名/categories/java,那么就可以采用PUT方法
- 客户端不一定都支持这些HTTP方法吧?
比较古老的基于浏览器的客户端,只能支持GET和POST两种方法。
在实践上,客户端和服务端都可能需要做一些妥协。例如rails框架就支持通过隐藏参数method=DELETE来传递真实的请求方法, 而像Backbone这样的客户端MVC框架则允许传递method传输和设置X-HTTP-Method-Override头来规避这个问题。
- 统一接口是否意味着不能扩展带特殊语义的方法?
统一接口并不阻止你扩展方法,只要方法对资源的操作有着具体的、可识别的语义即可,并能够保持整个接口的统一性。
- 统一资源接口对URI有什么指导意义?
URI只应该来表示资源的名称,URI不应该使用动作来描述。例如,下面是一些不符合统一接口要求的URI:
-
GET /getUser/1
-
POST /createUser
-
PUT /updateUser/1
-
DELETE /deleteUser/1
-
直接忽视缓存可取吗?
HTTP响应里增加这样一个报头: Cache-control: no-cache。 失去了高效的缓存与再验证的支持(使用Etag等机制)。
对于客户端来说,应该充分利用现有的缓存机制,以免每次都重新获取表示。
2. 3 资源的表述
客户端获取的只是资源的表述而已。
资源的表述包括数据和描述数据的元数据,例如,HTTP头"Content-Type" 就是这样一个元数据属性。
服务端提供表述形式,客户端可以通过Accept头请求一种特定格式的表述,服务端则通过Content-Type告诉客户端资源的表述形式。
在URI里边带上版本号
有些API在URI里边带上版本号,例如:
- http://api.example.com/1.0/foo
- http://api.example.com/1.2/foo
- http://api.example.com/2.0/foo
就应该只是用一个URL,并通过Accept头部来区分
还是以github为例,它的Accept的完整格式是:application/vnd.github[.version].param[+json]
对于v3版本的话,就是Accept: application/vnd.github.v3。
同理可以使用使用下面的头部:
- Accept: vnd.example-com.foo+json; version=1.0
- Accept: vnd.example-com.foo+json; version=1.2
- Accept: vnd.example-com.foo+json; version=2.0
使用URI后缀来区分表述格式
像Flask框架,就支持使用/users.xml或/users.json来区分不同的格式。
如何处理不支持的表述格式
它应该返回一个HTTP 406响应,表示拒绝处理该请求。
2. 4 资源的链接
“超媒体即应用状态引擎(hypermedia as the engine of application state)”。 超媒体是什么?
当你浏览Web网页时,从一个连接跳到一个页面,再从另一个连接跳到另外一个页面,就是利用了超媒体的概念:把一个个把资源链接起来.
github获取某个组织下的项目列表的请求,可以看到在响应头里边增加Link头告诉客户端怎么访问下一页和最后一页的记录。 而在响应体里边,用url来链接项目所有者和项目地址。
2. 5 状态的转移
REST原则中的无状态通信原则。初看一下,好像自相矛盾了,既然无状态,何来状态转移一说?
其实,这里说的无状态通信原则,并不是说客户端应用不能有状态,而是指服务端不应该保存客户端状态。
2. 5.1 应用状态与资源状态
实际上,状态应该区分应用状态和资源状态,客户端负责维护应用状态,而服务端维护资源状态。
客户端与服务端的交互必须是无状态的,并在每一次请求中包含处理该请求所需的一切信息。
服务端不需要在请求间保留应用状态,只有在接受到实际请求的时候,服务端才会关注应用状态。
这种无状态通信原则,使得服务端和中介能够理解独立的请求和响应。
在多次请求中,同一客户端也不再需要依赖于同一服务器,方便实现高可扩展和高可用性的服务端。
但有时候我们会做出违反无状态通信原则的设计,例如利用Cookie跟踪某个服务端会话状态,常见的像J2EE里边的JSESSIONID。
这意味着,浏览器随各次请求发出去的Cookie是被用于构建会话状态的。
当然,如果Cookie保存的是一些服务器不依赖于会话状态即可验证的信息(比如认证令牌),这样的Cookie也是符合REST原则的。
2. 5.2 应用状态的转移
客户端跟踪服务端的会话状态,在超媒体下 进行进行变迁,进入下一状态.
详细:
"会话"状态不是作为资源状态保存在服务端的,而是被客户端作为应用状态进行跟踪的。
客户端应用状态在服务端提供的超媒体的指引下发生变迁。
服务端通过超媒体告诉客户端当前状态有哪些后续状态可以进入。
这些类似"下一页"之类的链接起的就是这种推进状态的作用——指引你如何从当前状态进入下一个可能的状态。
3. 总结
现在广东XXX版本、XXX等项目中均使用传统的RPC、SOAP方式的Web服务,而移动南方基地XXXX项目的后台, 虽然采用了JSON格式进行交互,但还是属于RPC风格的。本文从资源的定义、获取、表述、关联、状态变迁等角度, 试图快速理解RESTful架构背后的概念。RESTful架构与传统的RPC、SOAP等方式在理念上有很大的不同,希望本文能对各位理解REST有所帮助。
续状态可以进入。
这些类似"下一页"之类的链接起的就是这种推进状态的作用——指引你如何从当前状态进入下一个可能的状态。
3. 总结
现在广东XXX版本、XXX等项目中均使用传统的RPC、SOAP方式的Web服务,而移动南方基地XXXX项目的后台, 虽然采用了JSON格式进行交互,但还是属于RPC风格的。本文从资源的定义、获取、表述、关联、状态变迁等角度, 试图快速理解RESTful架构背后的概念。RESTful架构与传统的RPC、SOAP等方式在理念上有很大的不同,希望本文能对各位理解REST有所帮助。
https://www.runoob.com/w3cnote/restful-architecture.html