REST 是一种应用架构风格,不是一种标准,是面向资源架构(ROA)风格,与具体技术平台无关,REST架构的应用未必建立在Web之上,与之对应的是传统的Web Service 采用的面向操作的RPC架构风格。
一、基础概念
1、Web
为我们提供获取和操作网络资源的方式,资源包括文字、图片、音频、视频;核心体现在三个方面,HTTP、超文本、超媒体,HTTP为网络访问的标准协议,超文本和超媒体,规范了网络信息的表现形式,利用“链接”在相关资源节点间穿针引线结成一张非线性网,即Web。
2、网络协议
(1)、IP协议
处于网络层,无链接的网络协议,传输不可靠且无序,数据完整性不能保证。
- 不可靠性,即不能保证发送出去的数据报文成功到达目的地。
- 无序,即每次数据报文处理都是独立的。
- 数据不完整性,接收方没有根据报文段的校验和来检查数据在传输过程中是否被篡改。
(2)、TCP协议
位于网络层之上的传输层,基于连接的传输协议,解决了IP协议不可靠性,无序性,不完整性三个问题。数据交换双方在在数据报文传输前先建立一个双工连接,双方都可以用它来发送数据,在报文传输结束后关闭连接。
- 可靠性,借助“接收确认”和“超时重传” 机制来实现,发送方有个缓冲区,用来存放发送出去但还未确认的保文,超过指定时限会重发,接收到确认消息就在缓冲区删除。
- 有序性,报文编号机制,每个数据报文都有个编号,其体现了报文的顺序,接收方接收到一个报文后,根据其编号先确认其之前编号的报文都接收了,才往应用层提交报文数据,判断标准是其记录一个上次成功递交给应用层的报文编号,如果此次接收报文的编号和上次是连续的说明是有序的,否则说明之前还有报文未接收。接收方也有个缓冲区,用来存放接收但不能向应用层提交的保文,等之前发送的所有报文都接收到之后,按序号顺序往应用层提交报文,再从缓冲区中把其删除。另外,重复的报文会被丢弃,判断重 复的标准是,报文的序号小于最后递交给应用层的报文编号或者添加到缓冲区的报文编号。
- 数据完整性,报文段有个16位的校验和(Checksum),接收方可以根据它来确认数据在传输过程中是否被篡改。
- 报文不丢失,借助“流量控制”机制,发送和接收方缓冲区大小不一致会导致发送后的报文会丢失,如果发送方缓冲区比接收方大,其实根据缓冲区大小来发送报文的,因为缓冲区存放了发送的而且未接收的,接收方缓冲区满了后,不能处理后续接收的报文,所以接收方会及时通知发送方缓冲区剩余大小,发送方根据其来控制流量。
(3)、HTTP协议
位于传输层之上的应用层,其基于传输层的TCP可靠传输协议,提供了可靠数据传输的保障。是一种无状态的网络协议,采用简单的“请求/响应”消息交换模式,不会保存多次消息交换的会话状态,每次消息交换都是相互独立的,开始于请求发送,止于响应接
收,每次消息交换都是一次完整的事物。
IP协议根据IP地址定位数据目的地,TCP协议利用端口标识应用程序,应用程序间通讯需要指定IP地址和端口。
3、资源
(1)、定义
寄宿于Web(服务器)、通过HTTP协议获取或操作的“事物“,可以是磁盘上的文件、数据库里的记录,算法计算出的结果,可以使具体的事物,也可以是抽象的流程。
(2)、标识
可操作资源应该具有一个或多个唯一标识。标识的类型可以采用GUID,但是URI是达成共识的公认标准。URL和URN都是URI的一种表现形式。
(3)、URL
统一资源定位符,用来定义资源的唯一标识、位置、获取方式(协议,如HTTP)。
http://www.myhost.com/employees/001
001号员工资源的唯一标识,位置在www.myhost.com服务器上,通过HTTP协议获取
4、媒体类型
资源是一种数据,其承载了某种信息(内容含义,比如部门所有员工),相同的信息可以用不同数据形态或数据格式来展现,这种数据格式称为媒体类型,比如相同的部门员工,可以用XML格式,也可以用JSON格式来展示。
媒体类型又称MIME类型,常用的媒体类型有,text/html,text/xml,text/json,image/gif,audio/mp4,video/mp4.
5、HTTP方法
资源用URI来标识,对资源的操作类型通过Http方法来指定。常见方法有以下几种:
- Get:获取资源。
- Head:获取描述资源的元数据信息。
- Option:探测请求,确定某个目标地址的真实请求应该具有怎么样的约束,如采用怎么样的Http方法或必须携带怎么的自定义报头,然后根据其约束发送符合条件的请求。“跨域资源”的预检请求采用的就是Options方法。
- Post:添加一个新资源
- Put:添加或修改一个资源,存在就修改,否则添加,这里的修改是完整修改
- Patch:部分修改,补丁的意思
- Delete:删除资源
6、HTTP响应状态码
- 1XX:请求已被接受,并须继续处理,100-199
- 2XX:请求被成功处理, 200-299
- 3XX:重定向,需要客户端采取进一步的操作才能完成请求, 300-399
- 4XX:客户端错误,因客户端发生错误而妨碍了服务器的处理, 400-499
- 5XX:服务端错误, 500-599
7、HTTP报文
分请求报文和响应报文,内容都是包含,起始行、报头集合(空行表示结束)、主题内容,如
- 请求报文
GET http://www.myhost.com/employees/001 HTTP/1.1 起始行
Accept:text/html,application/xml... 报头集合
...
空行
空 主体内容
- 响应报文
HTTP/1.1 200 OK 起始行
Content-Type:text/html;charset=utf-8 报头集合
....
空行
<!DOCTYPE... 响应主题内容
8、超媒体/超文本
超文本是与其它数据有关联(Links)的数据。包含与其他文档链接的文档;选择链接时自动显示第二个文档。
超文本可利用引用链接其他不同类型(内含声音、图片、动画)的文件,这些具有多媒体操作的超文本,称为超媒体(HyperMedia),意指多媒体超文本(Multimedia Hypertext),即以多媒体的方式呈现相关文件信息。
超媒体的核心是利用“链接”将相关的信息结成一个非线性的网。
9、REST(表现/应用状态转换)
REST定位为“分布式超媒体应用”的架构风格。
应用状态即WEB应用的客户端状态。
资源在浏览器中以超媒体的形式呈现,通过单击超媒体中的链接可以获取其他相关资源或对当前资源进行相应处理,获取的资源或者针对资源处理的响应同样以超媒体形式再次呈现在浏览器上。
借助于超媒体这种特殊资源呈现方式,应用状态的转换体现为浏览器中呈现资源的转换,应用状态变为了可呈现的状态,应用状态的转换变成了可呈现的状态转换,即REST。
二、RESTful Web API 特性
对于多个应用采用的架构,我们只能说其中一个比其他的更具有REST风格。以下是ROA架构的Web API应该具备的基本特征,是指导方针。
1、URI标识资源
URI具有标志性、可读性、可寻址性。可表示单个资源,也可以表示多个资源的集合。
http://www.myhost.com/employees/001 编号为001的员工
http://www.myhost.com/sales/2017/12/03 2017年12月3号的销售额
http://www.myhost.com/orders/2017/q4 2017年第四季度的订单(资源集合)
2、使用链接关联相关资源
资源一般不是单独存在,与其他资源有某种关联,通过超文本/超媒体文档中的链接关联相关资源;我们是用URL来标识资源的,文档中的链接采用URL地址来链接到其他资源,其可以使资源地址,也可以是对资源的操作链接。比如一份XML文档中,包含一些其他资源的引用。客户端获取该文档在浏览器展现,并根据文档中引用生成一些链接,这些链接地址都是Web API 地址,供用户操作.
3、使用统一的标准资源操作接口(CRUD)
这是RESTful Web API 和RPC风格面向功能操作集合的SOAP Web Service的Web API区分的一个主要特征。URI中不包含动词,动词使用HTTP谓词(Method)。返回结果影响到接口的统一性,要求所有的返回码必须通过HTTP状态码来返回,另外,客户端从服务端取得的不是资源本身,而是资源的表现形式。
标准接口即为不同(资源)Web API定义一致性的操作来管理各自的资源,从资源角度去组织接口,而且不同资源操作都一致,不像RPC风格的Web API 只是从功能操作角度去考虑和组织操作。
一致的操作即CRUD,如下
public class ResourceService
{
public IEnumerable<Resource> Get();
public void Create(Resource resource);
public void Update(Resource resource);
public void Delete(string id);
}
另外,一个Web API 来管理角色的增删查,以及建立和删除角色和用户之间的映射关系。RPC、SOAP风格的Web API 定义如下:
public class RoleService
{
public IEnumerable<string> GetAllRoles();
public void CreateRole(string roleName);
public void DeleteRole(string roleName);
public void AddRolesInUser(string userName, string[] roleNames);
public void RemoveRolesFromUsers(string userName, string[] roleNames);
}
其实,其中包含两种资源,角色和角色委派,改成统一接口
public class RoleService
{
public IEnumerable<string> GetAllRoles();
public void CreateRole(string roleName);
public void DeleteRole(string roleName);
}
public class RoleAssignmentService
{
public void Create( RoleAssignment roleName);
public void Delete( RoleAssignment roleName);
}
统一接口方法名,类名隐含了资源类别,不同资源操作,组织到不同类中,采用统一的接口,而不是像RPC风格接口,统一放一个类里,通过不同方法名区分。
4、使用标准的HTTP方法
常用就是前边列出的其中Http方法,比如新增资源
POST http://www.myhost/employees HTTP/1.1,请求主体中附上资源内容
另外,HTTP方法具有两个基本特性,安全性和幂等性。
GET、HEAD、OPTIONS是安全方法,只是获取数据,没有边界效应或副作用,另外四个方法,POST、PUT、PATCH、DELETE会导致服务器资源变化,所以是不安全的。
幂等性,即发送一次和多次请求引起的边界效应或副作用是一样的。设计API时候,尽量根据请求HTTP方法的幂等性来决定处理逻辑。
GET、HEAD、OPTIONS是幂等方法,DELETE和PATCH也是幂等的,都是对现有的某个资源操作,多次操作效果都一样,POST多次操作会增加多个资源,所以不是幂等的,PUT一般情况下也是幂等的,因为第一次不存在时候会添加,第二次操作存在了只会修改,对于PUT在特殊情况下是非幂等的,对于每次修改操作都修改一个版本号,但是有时候这种非幂等性不是非常重要。
5、多种资源表示方式(内容协商机制)
资源根据媒体类型有不同的表现形式,每次请求可以设定需要哪种资源的表示形式,如是需要XML还是JSON格式的资源。
通常是利用”内容协商机制”来实现,在请求报头,Accept和Accept-language设置它所希望的资源表示,即媒体类型和语言。
还有种方式是在URL中包含资源表示。
6、无状态性
RESTful只维护服务器上资源状态,而不维护客户端的状态;Web API 不记录客户端的以前的请求状态,其由客户端自己来维护。如分页号,Web API 不会知道上次的请求分页号,它只会根据传入的当前分页号来处理,上次分页号有客户端自己处理。是无状态的,减除了客户端亲和度,集群环境中有效实施负载均衡,每一台部署了WEB API接口的服务器对每个客户端都是等效的。
三、如何设计友好的REST API
1、URI
URI一般由名称和id组成
(1)、单层资源:
/users 所有的用户列表
/users/1 id为1的用户
注意:名称为复数users , id最好为uuid,使用简单、无锁,分布式系统中独立处理,各自插入数据,不用担心id冲突。
(2)、多层资源
/users/1/files 用户1的所有文件
/users/1/files/2 用户1的文件中id为2的文件
层数和参数不要太多,使用起来不方便,2层足够
(3)、查询参数不要放在URI中
/users/age/20/60 不好
/users?minAge=20&maxAge=60 写成url参数
2、资源拆分
资源与领域类具有相当高的对应关系。
3、资源命名
(1)、资源名称应该是名词或动名词形式,不要使用动词
比如创建和注销安全令牌 设计成 POST /tokens DELETE /tokens/{id}
(2)、注意名称的表意性,恰如其分的表示业务含义,不要太啰嗦
files而不要写成userFiles 太啰嗦
(3)、不要使用缩写词,避免使用有歧义或自创的所写词。
4、方法
(1)、安全性
安全性指的是一项操作不会改变资源的状态,该操作应该把资源看成只读。GET、HEAD是安全的,POST、PUT、DELETE、PATCH是不安全的。
但是,不是说不改变资源的状态,就不能改变服务器任何东西,比如GET方法中可以记录日志,不违反规范。
一般对安全的请求进行缓存,所以可以充分利用互联网的各级缓存设施。
如果你的GET方法会改变资源状态,很容易遭受到攻击,比如,GET请求进行投票,这样把GET方法的地址发到论坛或QQ群里,有人点击,就可以投票了。
(2)、幂等性
意义在于安全的自动重试刚才的操作,而不用担心破坏业务逻辑。
例如,浏览器中普通页面可以随意刷新,表单提交的页面刷新会弹出一个警告,问你是否确实要重试,这是因为POST操作是不幂等的,自动重试会导致预期之外的结果。
GET是幂等的,无论执行多少次都没区别,
POST不是幂等的,再执行一次会创建一个新的资源
DELETE 幂等的 ,用相同的数据去更新资源,无论执行多少次,其效果都一样
5、返回值
REST风格中,成功、失败等错误码应该在响应头中作为HTTP Status code 返回,不能在响应体中返回,这是HTTP规范也是REST规范要求。
我们可以在前端写一个过滤层,对服务端的错误信息进行统一处理,AOP拦截。