REST 定义了一组体系架构原则,您可以根据这些原则设计以系统资源为中心的 Web 服务,包括使用不同语言编写的客户端如何通过 HTTP 处理和传输资源状态。 如果考虑使用它的 Web 服务的数量,REST 近年来已经成为最主要的 Web 服务设计模型。 事实上,REST 对 Web 的影响非常大,由于其使用相当方便,已经普遍地取代了基于 SOAP 和 WSDL 的接口设计。
REST 这个概念于 2000 年由 Roy Fielding 在就读加州大学欧文分校期间在学术论文“Architectural Styles and the Design of Network-based Software Architectures”(请参见参考资料以获取此论文的链接)首次提出,他的论文中对使用 Web 服务作为分布式计算平台的一系列软件体系结构原则进行了分析,而其中提出的 REST 概念并没有获得现在这么多关注。 多年以后的今天,REST 的主要框架已经开始出现,但仍然在开发中,因为它已经被广泛接纳到各个平台中,例如通过 JSR-311 成为了 Java™ 6 不可或缺的部分。
本文认为,对于今天正在吸引如此多注意力的最纯粹形式的 REST Web 服务,其具体实现应该遵循四个基本设计原则:
- 显式地使用 HTTP 方法。
- 无状态。
- 公开目录结构式的 URI。
- 传输 XML、JavaScript Object Notation (JSON),或同时传输这两者。
下面几个部分将详述这四个原则,并提供技术原理解释,说明为什么这些原则对 REST Web 服务设计人员非常重要。
基于 REST 的 Web 服务的主要特征之一是以遵循 RFC 2616 定义的协议的方式显式使用 HTTP 方法。例如,HTTP GET 被定义为数据产生方法,旨在由客户端应用程序用于检索资源以从 Web 服务器获取数据,或者执行某个查询并预期 Web 服务器将查找某一组匹配资源然后使用该资源进行响应。
REST 要求开发人员显式地使用 HTTP 方法,并且使用方式与协议定义一致。 这个基本 REST 设计原则建立了创建、读取、更新和删除(create, read, update, and delete,CRUD)操作与 HTTP 方法之间的一对一映射。 根据此映射:
- 若要在服务器上创建资源,应该使用 POST 方法。
- 若要检索某个资源,应该使用 GET 方法。
- 若要更改资源状态或对其进行更新,应该使用 PUT 方法。
- 若要删除某个资源,应该使用 DELETE 方法。
许多 Web API 中所固有的一个令人遗憾的设计缺陷在于将 HTTP 方法用于非预期用途。 例如,HTTP GET 请求中的请求 URI
通常标识一个特定的资源。 或者,请求 URI 中的查询字符串包括一组参数,这些参数定义服务器用于查找一组匹配资源的搜索条件。 至少,HTTP/1.1 RFC
是这样描述 GET 方法的。 但是在许多情况下,不优雅的 Web API 使用 HTTP GET 来触发服务器上的事务性操作——例如,向数据库添加记录。
在这些情况下,GET 请求 URI 属于不正确使用,或者至少不是以基于 REST 的方式使用。 如果 Web API 使用 GET
调用远程过程,则应该类似如下:
GET /adduser?name=Robert HTTP/1.1
这不是非常优雅的设计,因为上面的 Web 方法支持通过 HTTP GET 进行状态更改操作。 换句话说,该 HTTP GET 请求具有副作用。 如果处理成功,则该请求的结果是向基础数据存储区添加一个新用户——在此例中为 Robert。 这里的问题主要在语义上。 Web 服务器旨在通过检索与请求 URI 中的路径(或查询条件)匹配的资源,并在响应中返回这些资源或其表示形式,从而响应 HTTP GET 请求,而不是向数据库添加记录。 从该协议方法的预期用途的角度看,然后再从与 HTTP/1.1 兼容的 Web 服务器的角度看,以这种方式使用 GET 是不一致的。
除了语义之外,GET 的其他问题在于,为了触发数据库中的记录的删除、修改或添加,或者以某种方式更改服务器端状态,它请求 Web 缓存工具(爬网程序)和搜索引擎简单地通过对某个链接进行爬网处理,从而意外地做出服务器端更改。 克服此常见问题的简单方法是将请求 URI 上的参数名称和值转移到 XML 标记中。 这样产生的标记是要创建的实体的 XML 表示形式,可以在 HTTP POST 的正文中进行发送,此 HTTP POST 的请求 URI 是该实体的预期父实体(请参见清单 1 和 2):