理解Restful架构
REST的由来
在互联网行业,实践总是走在理论的前列。Web发展至今,面向静态文档的HTTP/1.0协议已经无法满足Web应用的开发需求,因此需要设计新版本的HTTP协议。当前的发展趋势,前端设备层出不穷,因此,必须有一种统一的机制,方便不同的前端设备与后端进行通信。Roy Fielding(Apache软件基金会的合作创始人) 在他的博士学位论文Architectural Styles and the Design of Network-based Software Architectures中,Fielding更为系统、严谨地阐述了这套理论框架,并且使用这套理论框架推导出了一种新的架构风格,并且为这种架构风格取了一个令人轻松愉快的名字“REST”——Representational State Transfer(表述性状态转移)的缩写。
Rest架构风格最重要的架构约束
- 客户-服务器(Client-Server):通信只能由客户端单方面发起,表现为请求-响应的形式。
- 无状态(Stateless):通信的会话状态(Session State)应该全部由客户端负责维护。
- 缓存(Cache):响应内容可以在通信链的某处被缓存,以改善网络效率。
- 统一接口(Uniform Interface):通信链的组件之间通过统一的接口相互通信,以提高交互的可见性。
- 分层系统(Layered System):通过限制组件的行为(即,每个组件只能“看到”与其交互的紧邻层),将架构分解为若干等级的层。
- 按需代码(Code-On-Demand,可选):支持通过下载并执行一些代码(例如Java Applet、Flash或JavaScript),对客户端的功能进行扩展。
REST详解
如果深入理解REST,需要理解REST的五个关键词
- 资源(Resource)
- 资源的表述(Representation)
- 状态转移(State Transfer)
- 统一接口(Uniform Interface)
- 超文本驱动(Hypertext Driven)
什么是资源?
资源是一种看待服务器的方式,即,将服务器看作是由很多离散的资源组成。每个资源是服务器上一个可命名的抽象概念。因为资源是一个抽象的概念,所以它不仅仅能代表服务器文件系统中的一个文件、数据库中的一张表等等具体的东西,可以将资源设计的要多抽象有多抽象,只要想象力允许而且客户端应用开发者能够理解。与面向对象设计类似,资源是以名词为核心来组织的,首先关注的是名词。一个资源可以由一个或多个URI来标识。URI既是资源的名称,也是资源在Web上的地址。对某个资源感兴趣的客户端应用,可以通过资源的URI与其进行交互。
什么是资源的表述?
资源的表述是一段对于资源在某个特定时刻的状态的描述。可以在客户端-服务器端之间转移(交换)。资源的表述可以有多种格式,例如HTML/XML/JSON/纯文本/图片/视频/音频等等。资源的表述格式可以通过协商机制来确定。请求-响应方向的表述通常使用不同的格式。
什么是状态转移?
状态转移(state transfer)与状态机中的状态迁移(state transition)的含义是不同的。状态转移说的是:在客户端和服务器端之间转移(transfer)代表资源状态的表述。通过转移和操作资源的表述,来间接实现操作资源的目的。
什么是统一接口?
REST要求,必须通过统一的接口来对资源执行各种操作。对于每个资源只能执行一组有限的操作。以HTTP/1.1协议为例,HTTP/1.1协议定义了一个操作资源的统一接口,主要包括以下内容:
7个HTTP方法:GET/POST/PUT/DELETE/PATCH/HEAD/OPTIONS
HTTP头信息(可自定义)
HTTP响应状态代码(可自定义)
一套标准的内容协商机制
一套标准的缓存机制
一套标准的客户端身份认证机制
REST还要求,对于资源执行的操作,其操作语义必须由HTTP消息体之前的部分完全表达,不能将操作语义封装在HTTP消息体内部。这样做是为了提高交互的可见性,以便于通信链的中间组件实现缓存、安全审计等等功能。
什么是超文本驱动?
“超文本驱动”又名“将超媒体作为应用状态的引擎”(Hypermedia As The Engine Of Application State,来自Fielding博士论文中的一句话,缩写为HATEOAS)。将Web应用看作是一个由很多状态(应用状态)组成的有限状态机。资源之间通过超链接相互关联,超链接既代表资源之间的关系,也代表可执行的状态迁移。在超媒体之中不仅仅包含数据,还包含了状态迁移的语义。以超媒体作为引擎,驱动Web应用的状态迁移。通过超媒体暴露出服务器所提供的资源,服务器提供了哪些资源是在运行时通过解析超媒体发现的,而不是事先定义的。从面向服务的角度看,超媒体定义了服务器所提供服务的协议。客户端应该依赖的是超媒体的状态迁移语义,而不应该对于是否存在某个URI或URI的某种特殊构造方式作出假设。一切都有可能变化,只有超媒体的状态迁移语义能够长期保持稳定。
Rest风格6个的主要特征
- 面向资源(Resource Oriented)
- 可寻址(Addressability)
- 连通性(Connectedness)
- 无状态(Statelessness)
- 统一接口(Uniform Interface)
- 超文本驱动(Hypertext Driven)
REST架构风格可以为Web开发者带来三方面的利益:
简单性
采用REST架构风格,对于开发、测试、运维人员来说,都会更简单。可以充分利用大量HTTP服务器端和客户端开发库、Web功能测试/性能测试工具、HTTP缓存、HTTP代理服务器、防火墙。这些开发库和基础设施早已成为了日常用品,不需要什么火箭科技(例如神奇昂贵的应用服务器、中间件)就能解决大多数可伸缩性方面的问题。
可伸缩性
充分利用好通信链各个位置的HTTP缓存组件,可以带来更好的可伸缩性。其实很多时候,在Web前端做性能优化,产生的效果不亚于仅仅在服务器端做性能优化,但是HTTP协议层面的缓存常常被一些资深的架构师完全忽略掉。
松耦合
统一接口+超文本驱动,带来了最大限度的松耦合。允许服务器端和客户端程序在很大范围内,相对独立地进化。对于设计面向企业内网的API来说,松耦合并不是一个很重要的设计关注点。但是对于设计面向互联网的API来说,松耦合变成了一个必选项,不仅在设计时应该关注,而且应该放在最优先位置。
Restful API设计
在 REST 提出多年来,当前对于 REST 风格的应用最多的即是 Restful API 。
Restful API 一般分为对外和对内。对外的 Restful API 为面向公网的公共服务接口,此类接口一般可以通过公网直接访问,或是经过一定的安全认证后通过公网访问。而对内的 Restful API 则主要是一整套系统内部各个子系统或模块之间交互的标准接口,相对于对外的 Restful API 接口,内部 API 接口更加标准化。
按照 REST 的要求,Restful API 的设计可以总结出以下的一些具体要求。
HTTPS + 域名
Restful API 的无状态性,要求客户端需要在调用接口时传入足够的信息以供服务端用于操作指定的资源,这就有可能使得接口数据在网络传输过程中遭到拦截导致更多的数据泄漏。因此在提供 Restful API ,特别是对外的 API 时,应当尽可能的使用 HTTPS 协议,以确保传输过程的安全。
另一方面,在 API 地址中使用域名,可以进一步解耦服务端与客户端,服务端可以更加容易的迁移和扩展,而不会影响服务端的使用。
实际应用过程中,使用 HTTPS 协议,更多应用与对外的 Restful API 接口,而对内网的 Restful API 来说,可以在信任内网安全的前提下,使用 HTTP 协议,以降低复杂度,提高效率。
URL 指向资源,HTTP 动词指向操作
按照 REST 的要求,Restful API 的 URL 地址应指向具体的一个资源,例如用户 user 。URL 中应当只包含资源名词,不应该包含指向操作的动词,例如新建、查询、修改、删除等。具体操作通过 HTTP 动词( GET / POST / PUT / DELETE )指定。
指定 API 版本号
在设计 Restful API 时,特别是对外的 API ,通常需要考虑 API 多版本的问题,因为 API 会进行升级,而客户端则处于不可控状态,可能无法及时对 API 调用过程进行配合升级。因此,服务端需要提供对不同版本 API 的支持,同时,客户端在调用 API 时也需要指定特定的版本号,以确保调用过程正常进行。
版本号的指定,可以在 URL 中,也可以在 HTTP 头信息中。
指定参数
在 Restful API 请求中,可能需要根据不同的情况进行过滤,需要增加操作参数。一般来说,针对 GET 和 DELTE 请求需要增加操作参数的情况较多,而 POST 和 PUT 更多的是通过 HTTP 报文体提供操作数据信息。
指定参数可以通过两种方式:URL 地址参数和 ? 参数。
例如
使用 JSON 作为返回数据格式
例如:
使用安全认证机制
在使用 Restful API ,特别是对公网开放的 Restful API,通常需要通过一定的安全认证机制来进行实现访问控制。目前主流的方案是通过 OAuth2.0 实现安全认证。
Restful API 使用场景
根据 Restful API 的特定,其应用场景可以参考以下的场景:
资源集中型服务,例如针对用户的信息查询,针对订单的信息查询的等,这类型服务以资源实体为中心,操作大多为简单的 CRUD 操作,业务逻辑简单。
访问量大,且对访问时效要求比较高的服务。Restful API 相对于 SOAP WebService 来说,数据量更小,解析更快,在网络环境下能够提高访问的速度和承载能力。
面向公网的,且安全性要求较低的开放型 API 服务。这类服务通常由开发者向公网的所有使用者开放,Restful API 的形式能够简化服务调用过程,提高访问效率。
对于复杂业务操作,例如保全申请提交,理赔申请提交等,使用 Restful API 形式难以进行设计,因此此类的业务可以使用传统 RPC 的接口形式进行设计,SOAP WebService 或者 HTTP/JSON 形式的 RPC 都是可行的选择,使用 RPC 形式反而会更加简单。
Restful API 优势
Restful API 充分利用了 HTTP 协议的设计,使用面向资源的接口设计,相对于传统 RPC 降低了接口设计的复杂度。
例如,使用传统 RPC 形式设计针对用户对象的 CRUD 操作:
在以上实例中,通过 HTTP 动词指定了不同的 CRUD 操作,将接口 URL 简化为了同一个地址,仅需要改变 HTTP 动词即可实现不同的操作。
另一方面,相对于 SOAP/XML 形式的 RPC 服务,Restful API 采用 HTTP/JSON 的形式传递数据,降低了传输数据量,同时提高了数据解析的效率,单位时间内的负载能力会高于 SOAP WebService 服务。
例如,对于 SOAP WebService 来说,基本的请求响应格式如下:
而对于 Restful API 来说,其请求格式符合 HTTP 协议的要求,返回格式则符合标准的 JSON 格式即可。
Restful API 劣势
Restful API 面向资源设计接口,而对于一些复杂操作来说,接口设计难度将远大于 RPC 形式。
例如,用户登录验证,使用 RPC 形式设计接口如下:
https://open.domain.com/app/checkUser
在以上实例中,对于 Restful API 而言,很难将该业务归类为 HTTP 动词中的某一种。又比如“下单”这一功能,它涉及到了订单、用户、支付账户等多个资源实体的多种操作,因此同样也难以设计为 Restful API 。
Restful API 具备高效简洁的特点,但这也因此造成 Restful API 没有类似于 SOAP 协议的规范性协议,Restful API 中的数据格式、标准、安全性等都需要由开发者决定,这也就造成了无法建立统一的 Restful API 标准,作为客户端可能需要适配多种格式的 Restful API 。
选择哪种方式的接口设计,需要根据实际的应用情况进行调整,没有最好的,只有最合适的
参考文章
https://segmentfault.com/a/1190000006735330
http://www.infoq.com/cn/articles/understanding-restful-style