什么是RESTful?

  • 一、什么是RES风格?
  • 1.1 资源
  • 1.2 REST架构特征
  • 1.3 SOAP与REST的比较
  • 二、为什么要使用REST风格?
  • 三、如何设计REST风格API?
  • 1 RESTful API 请求
  • 1.1 HTTP 动词
  • 1.2 URL(宾语)必须是名词
  • 1.3 过滤信息(Filtering)
  • 2 RESTful API 响应
  • 2.1 常用HTTP 状态码
  • 2.2 返回数据
  • 3 Richardson成熟度模型
  • 3.1 零级
  • 3.2 一级
  • 3.3 二级
  • 3.4 三级
  • 四. 案例


一、什么是RES风格?

REST是Representational State Transfer的首字母缩写,中文意思是表述性状态转移。REST描述了一个架构样式的网络系统,比如 web应用程序,REST并没有一个明确的标准,而更像是一种设计的风格。所以如果一个架构符合REST风格,就称它为 RESTful架构。

1.1 资源

REST是表述性状态转移的意思,那这是谁的表述性状态?这里缺少了一个主语:资源,完整的意思是资源的表述性状态转移。任何事物,只要有被引用到的必要,它就是一个资源。这是很宽泛的概念,在Web应用中,通过Web可操纵的事物都可视资源,比如磁盘上的文件,数据库里的记录。在Web应用中,资源的唯一标识就是URI,URI既可以看作是资源的地址,也可以看作是资源的名称。

客户端使用GETPOSTPUTDELETEPATCH 5个表示操作方式的动词对服务端资源进行操作:GET用来获取资源,POST用来新建资源(也可以用于更新资源),PUT用来更新资源,DELETE用来删除资源;

1.2 REST架构特征

  1. 客户端 - 服务器:通过将用户接口问题与数据存储问题分开,我们通过简化服务器组件来提高跨多个平台的用户接口的可移植性并提高可伸缩性。
  2. 无状态:从客户端到服务器的每个请求都必须包含理解请求所需的所有信息,并且不能利用服务器上任何存储的上下文。因此,会话状态完全保留在客户端上。
  3. 可缓存:缓存约束要求将对请求的响应中的数据隐式或显式标记为可缓存或不可缓存。如果响应是可缓存的,则客户端缓存有权重用该响应数据以用于以后的等效请求。
  4. 统一接口:通过将通用性的软件工程原理应用于组件接口,简化了整个系统架构,提高了交互的可见性。为了获得统一的接口,需要多个架构约束来指导组件的行为。REST由四个接口约束定义:资源识别; 通过陈述来处理资源; 自我描述性的信息; 并且,超媒体作为应用程序状态的引擎。
  5. 分层系统:分层系统风格允许通过约束组件行为来使体系结构由分层层组成,这样每个组件都不能“看到”超出与它们交互的直接层。
  6. 按需编码:REST允许通过以小程序或脚本的形式下载和执行代码来扩展客户端功能。这通过减少预先实现所需的功能数量来简化客户端。

1.3 SOAP与REST的比较

SOAP(Simple Object Access Protoco)简单对象访问协议是在分散或分布式的环境中交换信息的简单的协议,是一个基于 XML 的协议。

构建模块:
一条SOAP消息就是一个普通的XML文档,包含下列元素:
(1)必需的Envelope元素,可把此XML文档表示为一条SOAP消息;
(2)可选的Header元素,包含头部信息;
(3)必需的Body元素,包含所有的调用和响应消息;
(4)可选的Fault元素,提供有关在处理此消息发生错误的信息;

语法规则:
(1)SOAP消息必须用XML来编码;
(2)SOAP消息必须使用SOAP Envelope和SOAP Encoding命名空间;
(3)SOAP消息不能包含DTD引用;
(4)不能包含XML处理指令;

REST更容易开发,因为它利用了已经存在的Web,并且自由度有限(制作的选择更少,更简单)。SOAP提供了许多替代方案,开发起来也稍微困难一些,但提供了更多的替代方案和工作领域。

二、为什么要使用REST风格?

  • RESTful架构可以充分的利用 HTTP 协议的各种功能,是HTTP协议的最佳实践。
  • RESTful API是一种软件架构风格、设计风格,可以让软件更加清晰,更简洁,更有层次,可维护性更好。

如果我们不用REST风格,我们在代码中,只会用GET获取资源,用POST新增和修改资源。其实HTTP的method都有其独特意义。我们按照REST风格定义API,可以规范化接口访问方式,让API显得更加简洁明了,也减少前后端人员沟通时间。

如果不用RESTful:

获取人员
GET: /epam/user/getUserById?id=1

保存人员
POST: /epam/user/saveUser

删除人员
POST: /epam/user/deleteUserById?id=1

使用RESTful:

获取人员
GET: /epam/users/:id

创建人员
POST: /epam/users

修改人员全部信息
PUT: /epam/users/:id

修改人员部分信息
PATCH: /epam/users/:id

删除人员
DELETE: /epam/user/:id

三、如何设计REST风格API?

1 RESTful API 请求

RESTful API 请求 = 动词 + 宾语

  • 动词:使用五种 HTTP 方法,对应 CRUD 操作。
  • 宾语:URL 应该全部使用名词复数,可以有例外,比如搜索可以使用更加直观的 search。
  • 过滤信息(Filtering):如果记录数量很多,API应该提供参数,过滤返回结果。 ?limit=10 指定返回记录的数量 ?offset=10 指定返回记录的开始位置。

1.1 HTTP 动词

GET:   读取(Read)
POST:  新建(Create)
PUT:   更新(Update)
PATCH: 更新(Update),通常是部分更新
DELETE:删除(Delete)

1.2 URL(宾语)必须是名词

宾语就是 API 的 URL,是 HTTP 动词作用的对象。它应该是名词,不能是动词。比如,/articles这个 URL 就是正确的,而下面的 URL 不是名词,所以都是错误的。

/getAllCars
/createNewCar
/deleteAllRedCars

既然 URL 是名词,为了统一起见,建议都使用复数。

1.3 过滤信息(Filtering)

如果记录数量很多,服务器不可能都将它们返回给用户。API应该提供参数,过滤返回结果。

下面是一些常见的参数。

?limit=10:指定返回记录的数量
?offset=10:指定返回记录的开始位置。
?page=2&per_page=100:指定第几页,以及每页的记录数。
?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
?animal_type_id=1:指定筛选条件

2 RESTful API 响应

  • 客户端的每一次请求,服务器都必须给出回应。回应包括 HTTP 状态码数据两部分。
  • 客户端请求时,要明确告诉服务器,接受 JSON 格式,请求的 HTTP 头的 ACCEPT 属性要设成 application/json
  • 服务端返回的数据,不应该是纯文本,而应该是一个 JSON对象。服务器回应的 HTTP 头的 Content-Type 属性要设为 application/json
  • 错误处理 如果状态码是4xx,就应该向用户返回出错信息。一般来说,返回的信息中将 error 作为键名,出错信息作为键值即可。 {error: “Invalid API key”}
  • 认证 RESTful API 应该是无状态,每个请求应该带有一些认证凭证

2.1 常用HTTP 状态码

200 OK - [GET]:服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)。
201 CREATED - [POST/PUT/PATCH]:用户新建或修改数据成功。
202 Accepted - [*]:表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT - [DELETE]:用户删除数据成功。
400 INVALID REQUEST - [POST/PUT/PATCH]:用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的。
401 Unauthorized - [*]:表示用户没有权限(令牌、用户名、密码错误)。
403 Forbidden - [*] 表示用户得到授权(与401错误相对),但是访问是被禁止的。
404 NOT FOUND - [*]:用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的。
406 Not Acceptable - [GET]:用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)。
410 Gone -[GET]:用户请求的资源被永久删除,且不会再得到的。
422 Unprocesable entity - [POST/PUT/PATCH] 当创建一个对象时,发生一个验证错误。
500 INTERNAL SERVER ERROR - [*]:服务器发生错误,用户将无法判断发出的请求是否成功。

2.2 返回数据

response 的 body直接就是数据,不要做多余的包装。错误实例:

{"success":true, "data":{"id":1, "name":"周伯通"} }

有一种不恰当的做法是,即使发生错误,也返回200状态码,把错误信息放在数据体里面,就像下面这样。

{"status": "failure", "data": { "error": "Expected at least two items in list."} }

正确的做法是,状态码反映发生的错误,具体的错误信息放在数据体里面返回。下面是一个例子。

HTTP/1.1 400 Bad Request
Content-Type: application/json
{
  "error": "Invalid payoad.",
  "detail": {
    "surname": "This field is required."
  }
}

3 Richardson成熟度模型

Leonard Richardson分析了一百种不同的Web服务设计,并根据它们与REST的兼容程度将它们分为四类。这种REST服务划分模型用于识别其成熟度级别,称为Richardson成熟度模型。

Richardson使用三个因素来决定服务的成熟度,即URI,HTTP方法和HATEOAS (超媒体)。服务采用越多的这些技术,我们就认为它更成熟。Richardson将这些成熟度水平分为四个等级:零级、一级、二级和三级。

参考资料:Richardson Maturity Model

3.1 零级

不使用任何URI,HTTP方法和HATEOAS功能,HTTP仅作为远程传输的协议,要获取和提交数据,将请求发送到相同的URI,并且只能用POST方法。

举例:使用相同的URI和方法来获取和保存数据

获取用户:POST http://epam/user 保存用户:POST http://epam/user

3.2 一级

使用URI,HTTP方法和HATEOAS中的URI,当一个API可以区分不同的资源时,它使用多个URI,每一个URI都是特定的资源入口,但是还是只能用POST方法。

举例:
人员列表:POST http://epam/users 一个人员:POST http://epam/users/0003

3.3 二级

使用 URI,HTTP方法和HATEOAS中的URI和HTTP,正确的HTTP动词用于每个请求,它建议为了真正实现RESTful,必须在API中使用HTTP动词,对于每一个请求,返回正确的HTTP响应码。

获取人员  GET: /epam/users/:id ,获取的到返回200,没有则返回404

创建人员  POST: /epam/users

删除人员  DELETE: /epam/user/:id

3.4 三级

使用所有三个,即URI,HTTP和HATEOAS,它是二级和HATEOAS的组合。超媒体包含了客户端可能感兴趣的其它资源的URI链接,它的重点是告诉我们接下来可以做些什么,以及我们需要操作的资源的URI。

四. 案例

根据ID获取整个人员信息
GET: /epam/users/:id

根据ID获取人员地址信息
GET: /epam/users/:id/address

人员列表
GET: /epam/users

人员分页列表,根据name升序
GET: /epam/users?size=20&page=5&sortby=name&order=asc

创建人员
POST: /epam/users

修改人员全部信息
PUT: /epam/users/:id

修改人员部分信息
PATCH: /epam/users/:id

删除人员
DELETE: /epam/user/:id