网站内部相关文章:

RESTful 是什么

定义和术语

首先, 让我们带着疑问进行后续阅读:

  • 什么是 REST(表述性状态转移)风格?
  • 如何使用 REST 来指导现代 WEB 架构的设计和开发?
  • REST 解决了哪类问题?
  • REST 相比传统 WEB API, 其有哪些优点?

另外, 在了解 RESTful 之前, 最好提前先看一遍Roy Thomas Fielding博士的论文架构风格与基于网络的软件架构设计, 其非常全面清晰的讲解了:

  • 软件架构的基本组成以及评估一个软件风格架构属性
  • 基于网络的架构的分类及适用场景
  • 基于万维网的需求, 由基于网络的架构风格逐渐搭建的而出的 REST 风格以及优缺点

Roy Fielding博士是参与HTTP1.0和HTTP1.1协议的设计工作人员之一, HTTP1.1第一份草稿于是在 1996 年 1 月发布的, 经过了三年多时间的修订, 于 1999 年 6 月成为了 IETF 的正式规范. REST 论文则是 Fileding 在参与完HTTP/1.1协议的设计工作之后于 2000 的博士论文中提出, 其更为系统, 严谨地阐述了HTTP1.1理论框架, 并且使用这套理论框架推导出了一种新的架构风格, 这个架构风格就是 REST(表述性状态转移).

API 发展历史中我们简单的提及了 WEB 的发展历史, 但在万维网规模越来越大的时, 其产生了如下需求:

  • 如何为结构化的信息提供统一的, 一致的接口;
  • 如何在尽可能多的平台上获取各类信息;
  • 如何增量部署以支持不断新增的用户和新增的组织;

REST 于此时被提出以解决上述大部分需求, 这些需求可以解释为如下几个描述:

  1. 低门槛: 阅读者使用超媒体能够更加简单的进行信息的获取和更新, 创作者基于超文本语言相关编辑工具进行简单化的创作, 开发者基于一套通用的简单化的协议进行 web 应用的开发
  2. 可扩展性: 避免开发者或者运维相关人员陷入已部署系统的局限中, 并且能够跟随时间的变化, 需求的变化而方便的做出改变
  3. 分布式超媒体: 分布式超媒体支持大粒度的数据转移, 尽量减少用户可察觉的延时
  4. Internet 复杂环境造就的无法控制的可伸缩性: 整个 Internet 是开发运行的, 其本身就包含了无法预测的负载量, 特殊的不良格式或恶意构造的数据, 所以系统架构本身不应该期待客户端保持所有服务器的信息, 也不能期待服务器跨多个请求保持状态的信息, 并且组织之间存在着不同的信任边界, 如何界定信任边界, 如何安全的进行数据防护, 上面这些都是非常重要的问题
  5. web 应用的独立部署能力, 能够容易以一种部分的, 迭代的方式来部署架构元素

REST 视图

为了展示 REST 的设计原则, 需要使用三种视图, 从三个角度来呈现表述性状态转移, 在软件架构中我们了解到架构元素主要分为三种:

  • 组件(处理)
  • 连接器
  • 数据

有点类似的, 从整体性角度描述 REST 提供了三种视图:

  • Process View: 通过展示数据在系统中的流动路径, 得出组件之间的交互关系
  • Connector View: 聚焦于组件之间的通信机制
  • Data View: 展示了信息在组件之间流动时的应用状态
  1. 过程视图注重组件交互关系, 从这个角度来判断架构风格是否符合需求, 例如:
  • 关注点分离简化了组件的实现, 降低了连接器语义的复杂性, 改善了性能调优的效率且提高了纯服务器组件(pure server components)的可伸缩性
  • 分层约束允许在通信的不同地点引入中间组件而无须改变组件之间的接口, 同时还可以接入 cache 来改善性能, REST 通过具有自描述性来支持中间组件的处理.
  • 组件之间的交互独立于其他的交互, 使其无须了解整体的组件拓扑结构, 允许组件要么作为目的地要么作为中间组件.
  1. 连接器视图注重通信机制, 对定义通用资源接口的约束尤其感兴趣
  • 检查资源标识符, 以便为每个请求选择一个合适的通信机制
  • 组件之间的单一的, 通用的接口, 从而确保单个代理能够访问多个服务
  1. 数据视图注重应用状态
  • 每一个应用都是信息和控制的独立聚合体, 每一个应用都包含其自身的目标, 针对这些目标来测量系统的性能
  • 将所有控制状态浓缩在从交互的响应中接收到的表述之中, 通过使服务器无须维护当前请求之外的客户端状态,从而改善服务器的可伸缩性
  • 应用状态包括: 悬而未决的请求, 相连接的组件的拓扑结构, 连接器上活跃的请求, 请求响应中表述的数据流, 当用户代理接收到这些表述时对表述的处理

REST 元素

表述性状态转移(REST)风格是对分布式超媒体系统中的架构元素的一种抽象。REST 忽略了组件实现和协议语法的细节,以便聚焦于以下几个方面:

  • 组件的角色
  • 组件之间的交互之上的约束
  • 组件对重要数据元素的解释
  1. 数据元素

一个分布式超媒体系统的架构师仅拥有三种基本的选项:

  • 在数据的所在地对数据进行呈现,并向接收者发送一个固定格式的映象, 例如 CS 架构, 它使得与数据的真实性质有关的所有信息都被隐藏在数据发送者之中, 简化了客户端实现. 但其将大部分处理负担都放在发送者这边, 导致了可伸缩性的问题.
  • 将数据和呈现引擎封装起来并将两者一起发送给接收者, 例如可移动对象风格, 它提供了信息的隐藏, 对数据进行专门的处理. 但它将接收者的功能限制在了引擎所能预测的范围之内, 大幅增加需要转移的数据量.
  • 发送原始数据和一些描述数据类型的元数据,这样接收者就能够选择它们自己的呈现引擎, 其让发送者保持简单性和可伸缩性, 使得需要转移的数据最小化, 但是丧失了数据隐藏的优点,要求发送者和接收者都必须理解相同的数据类型

REST 则提供了上述三者选项的混合体:

  • 以一种数据格式转移资源的表述来进行通信, 该格式与一组标准数据格式之一相匹配, 基于接受者的不同能力来返回不同的格式, 提高了伸缩性.
  • 服务器发送由一个封装过的呈现引擎的标准数据格式中的指令组成的表述(HTML 文档, 图片等), 结合后两个选项, 既确保了伸缩性, 也减少了转移的数据量.

REST 的数据元素包含如下信息:

  • 资源: 一个超文本引用意图指向的概念上的目标
  • 资源标识符: URL、URN
  • 表述: HTML 文档、JPEG 图片
  • 表述元数据: 媒体类型、最后修改时间
  • 资源元数据: 源链接、alternates、vary
  • 控制数据: if-modified-since、cache-control 等
  1. 连接器

REST 使用多种不同的连接器类型来对访问资源和转移资源表述的活动进行封装。连接器代表了一个组件通信的抽象接口,通过提供清晰的关注点分离、 并且隐藏资源的底层实现和通信机制,从而改善了架构的简单性。

REST 的连接器包含如下信息:

  • 客户端: libwww、libwww-perl
  • 服务器: libwww、Apache API、NSAPI
  • 缓存: 浏览器缓存、Akamai 缓存网络
  • 解析器: 绑定(DNS 查找库)
  • 隧道: SOCKS、HTTP 连接之后的 SSL
  1. 组件

REST 组件根据它们在整个的应用动作(application action)中的角色来进行分类, 其包含如下信息:

  • 来源服务器(origin server): Apache httpd、微软 IIS
  • 网关(gateway): Squid、CGI、反向代理
  • 代理(proxy): CERN 代理、Netscape 代理、Gauntlet
  • 用户代理(user agent) : Netscape Navigator、Lynx、MOMspider

风格和属性

软件架构中我们讲解到风格和属性的概念, 一种架构约束产生一种架构风格, 约束必然产生相对应的几种属性(伸缩性, 简单性, 可修改性等等), 不同的架构风格拥有不同的属性, 也有可能相同的属性, 通过属性反过来评判某一种风格适用的场景以及适配的性能.

那么, 基于网络的架构风格是怎样的呢? 在 1.1 节我们已经介绍了万维网发展的几个需求, 下面我们讲解一下基于这些需求, REST 引入了哪些风格来解决这些问题.

基于 REST 的架构风格

Web 架构背后的设计基本原理,能够被描述为由一组应用于架构中元素之上的约束组成的架构风格。当将每个约束添加到进化中的风格时,会产生一些影响。通过检查这些影响, 我们就能够识别出 Web 的约束所导致的属性。然后就能够应用额外的约束来形成一种新的架构风格,这种风格能够更好地反映出现代 Web 架构所期待的属性。

  1. 空风格

在软件设计和建筑中, 人们对于架构设计的过程都持有两个共同的观点:

  • 一切从零开始, 使用熟悉的组件构建出一个架构直到满足需求
  • 从整体的系统需求出发, 增量的识别出各种约束, 并将它们应用于系统的元素之上, 其强调限制和对系统环境的理解

REST 就是使用后者发展而成, 随着增量地应用一组约束, 已应用的约束会将架构的过程视图区分开.

  1. Client-Server

通过客户端和服务器模式, 该风格的主要约束就是分离关注点, 其达到如下的效果:

  • 通过分离关注点原则, 分离用户接口和数据存储这两个关注点, 改善了用户接口跨多个平台的可移植性
  • 简化服务器组件, 改善了系统的可伸缩性
  • 关注点的分离允许组件独立地进化, 支持多个组织领域的 Internet 规模的需求
  1. stateless

客户端-无状态-服务器风格, 其添加了如下约束: 通信必须在本质上是无状态的, 从客户到服务器的每个请求都必

须包含理解该请求所必需的所有信息, 会话状态全部保存在客户端, 该约束产生了如下的效果:

  • 可见性: 监视系统不必为了确定一个请求的全部性质而去查看该请求之外的多个请求
  • 可靠性: 减轻了从局部故障中恢复的任务量
  • 可伸缩性: 不必在多个请求之间保存状态,从而允许服务器组件迅速释放资源, 服务器不必跨多个请求管理资源的使用

无状态本身也确保了后续系统分层更加便利, 当然, 无状态本身也增加了请求之间重复数据的发送量, 降低了网络性能, 降低了服务器对于一致的应用行为的控制, 服务器需要本身需要兼容多个协议, 应用自身需要确保多个客户端版本的语义的正确实现.

  1. Cache

客户端-缓存-无状态-服务器其添加了如下约束: 缓存, 缓存约束要求一个请求的响应中的数据被隐式地或显式地标

记为可缓存的或不可缓存的, 通过设置缓存开关, 动态的进行缓存设置:

  • 强制缓存: expires, cache-control(max-age, no-cache)
  • 弱缓存或者对比缓存: last-modified/if-modified-since, etag/if-nont-match, 其中后者权重更高
  1. 统一接口

这是 REST 的核心特征, 它强调组件之间要有一个统一的接口, 其应用软件工程原则, 将服务解耦, 简化整个系统架构, 其添加了如下约束:

  • 资源的识别: URI
  • 通过表述对资源执行的操作: GET, POST, CONNECT
  • 自描述的消息: 请求头, 请求体, 响应头, 响应体
  • 作为应用状态引擎的超媒体

这些约束在 1.3 节中已经提及.

  1. 分层系统

分层系统添加了如下约束: 分层, 确保每一层只能同相邻层进行交互, 通过设置边界大大的降低系统的复杂性, 提高了底层独立性, 另外还可以通过引入中间层, 如防火墙, 监视器提高可见性等.

当然, 分层系统类似无状态, 其增加了数据处理的开销和延迟, 降低了用户可觉察的性能, 当然, 这可以通过增加缓存约束来尽可能的减少性能损耗.

  1. 按需代码

增加按需代码风格: 一个客户端组件知道如何访问一组资源,但不知道如何处理它们。它向一个远程服务器发送对于如何处理资源的代码的请求,接收这些代码,然后在本地执行这些代码. 在某些安全性要求高的环境下, 该约束可能不是非常符合需求, 故只有当已知对于整个系统的某些领域有效的情况下,架构才会从 可选的约束得到好处.

其他术语

  1. 插拔式

Hot Plugging: 带电插拔,在电脑运作时插入硬件,配合某些软件,可以在不影响电脑系统本身运行情况下,随意的插入和拔出热设备,实现即插即用(Plug-and-Play).

pluggable aspects:对于某一个 API 或者功能而言,某些 pluggable 属性的增加、删除都不会影响整体的运行,类似 python 中的 default 参数变量一样.

其中 pluggable 可以为:attributes, instantiation methods, api decorators.

  1. Dispatch: 主动管理者:dispatcher,被动触发者:dispatch()
  • 它是应用程序的中央集线器、电话总机,根据不同的 actions 来执行、分发、分派任务。
  • 它是一个中间过滤和监控者,所有的 api 调用都需要经过它。
  • 它是一个 setup,它是一个 setDown。

python test

关于 unittest 这里不再描述,仅仅阐述 API 测试中 python 脚本的用途,高效的完成测试工作。

基本结构

  • 设置 token、其他 params
  • 设置 URL
  • 调用:rsp = requests.post(URL, data=params)

一些测试工具, 例如 python, http 插件, firefox 插件可以用于测试 REST 接口:

1
2
# 使用requests和urllib来模拟浏览器操作,对于每次调用都需要设置token或者其他信息
rsp = requests.post(url, data={'token':xxxx', ...})

http 插件: Postman, Insomnia

firefox 插件: Poster/RestClient/HttpRequester

测试方式

1
2
3
4
5
测试用例文件结构:
# NAME|TOKEN|ROUTE|PARAM|CODE
create user|master|/user.create|{'email': 'cn@cn.cn', 'password': '123456', ...}|2101
运行代码解析测试用例文件
循环执行基本结构即可。

参考