REST API 学习笔记
最早接触 REST 接口是在 CDMI,不过那时只是对REST有一个初步的认识。最近,偶然读到一篇《最佳实践:更好的设计你的 REST API》,对其有了比较深入的理解力。
1. REST 简介
REST 是英文 Representational State Transfer 的缩写,是一种基于 HTTP,URI,以及 XML 这些现有协议与标准的,针对网络应用的设计和开发方式。它可以降低开发的复杂度,提高系统的可伸缩性。
2. 一切皆资源,URI 组织资源
在REST构架的设计中,系统中的所有事物都被抽象为资源。 并且利用 URI 来组织资源,资源可以是一个个体,也可以是一个集合,例如:
http://example.com/users -- 一组用户
http://example.com/users/<user_id> -- 一个单一用户
一个 URI 指向一个资源,同一个资源可以有多个 URI,例如:
http://example.com/users/<user_id>/<ticket>
http://example.com/tickets/<ticket_id>
通过参数条件过滤,更准确地获取数据,可以通过 POST 参数,或者 URL 参数方式:
http://example.com/users?filter=age between (15, 18)
通过排序来更好的展现数据
http://example.com/users??sortOrder=asc&sortField=age
使用分页来处理大量数据
http://example.com/users?page=5&pagesize=50
对于某些特殊的情况下,可以把一些操作也定义为一种资源。例如,对资源加锁、修改文件等。
2. 使用 Http 的 Method
REST API 通过使用 Http 协议已有的 Method
- 使用 POST 方法在服务器上创建资源
- 使用 GET 方法从服务器检索某个资源或者资源集合
- 使用 PUT 方法对服务器的现有资源进行更新
- 使用 DELETE 方法删除服务器的某个资源
3. 理解和使用内容协商
使用 URL 参数进行内容协商
http://example.com/users?format=xml
使用 Accept 头进行内容协商
更为标准的内容协商方式是使用 HTTP 头。我们通常使用 Accept 来设置我们接受的返回结果的内容格式,用 Accept-Charset 来设置字符集,用 Accept-Encoding 来设置数据传输格式,用 Accept-Language 来设置语言。
使用 URI 模式进行内容协商
http://example.com/users/xml
3. 正确的使用 HTTP 响应代码
作为 API 的设计者,正确的将 API 执行结果和失败原因用清晰简洁的方式传达给客户程序是十分关键的一步。 我们确实可以在 HTTP 的相应内容中描述是否成功,如果出错是因为什么, 然而, 这就意味着用户需要进行内容解析,才知道执行结果和错误原因。因此,HTTP 响应代码可以保证客户端在第一时间用最高效的方式获知 API 运行结果,并采取相应动作。 下表列出了比较常用的响应代码。
HTTP响应代码 | 代码含义 |
---|---|
200 | 已创建,请求成功且服务器已创建了新的资源。 |
201 | 是否只显示处于警告状态的应用实例 |
301 | 重定向 , 请求的网页已被永久移动到新位置。服务器返回此响应时,会自动将请求者转到新位置。 |
302 | 重定向 , 请求的网页临时移动到新位置,但求者应继续使用原有位置来进行以后的请求。302 会自动将请求者转到不同的临时位置。 |
304 | 未修改,自从上次请求后,请求的网页未被修改过。服务器返回此响应时,不会返回网页内容。 |
400 | 错误请求 , 服务器不理解请求的语法。 |
401 | 未授权 , 请求要求进行身份验证。 |
403 | 已禁止 , 服务器拒绝请求。 |
404 | 未找到 , 服务器找不到请求的网页。 |
405 | 方法禁用 , 禁用请求中所指定的方法。 |
406 | 不接受 , 无法使用请求的内容特性来响应请求的网页。 |
408 | 请求超时 , 服务器等候请求时超时。 |
410 | 已删除 , 如果请求的资源已被永久删除,那么,服务器会返回此响应。 |
412 | 未满足前提条件 , 服务器未满足请求者在请求中设置的其中一个前提条件。 |
415 | 不支持的媒体类型 , 请求的格式不受请求页面的支持。 |
500 | 内部服务器错误。 |
4. 使用 HTTP 头处理缓存和并发
由于REST接口是无状态的,所以对于缓存和并发控制更加容易。
使用 HTTP 头进行缓存处理
缓存控制通常是需要客户端,缓存服务器 / 代理服务器与业务服务器一起发生作用。 HTTP 头中有 “Cache-control” 字段来控制如何使用缓存,常见的取值有 private、no-cache、max-age、must-revalidate 等。比如当你给返回的数据内容设置 max-age=600,那么当用户隔了 30 秒再次请求的时候,就不会导致重新请求后台数据。 另外,也可以通过 “Expires” 字段来指定内容过期时间,在此时间前的请求都不会导致后台程序重新请求数据。
条件请求与电子标签
很多时候,数据内容可能会几个小时甚至几天都不会发生变动,这个时候根据请求时间间隔来控制缓存,就不能满足系统的需求了。通过支持条件请求与电子标签,可以帮助我们来解决这个问题。 当用户请求数据内容时,系统在返回数据的同时,在 HTTP 头中,将返回根据服务器内容的最后修改时间 Last-Modified,或者根据服务器内容生成电子标签 ETag。 当用户再次请求数据时,就可以在 HTTP 请求中使用 If-Modified-Since 或者 If-None-Match 头信息,把上次请求得到的时间戳或者电子标签传给服务器。当收到一个有条件请求的 HTTP 头的 REST 请求的时候,我们的程序需要将收到的时间戳或者电子标签与当前内容作比较,就可以很容易的知道用户请求的数据内容在这段时间是否发生过修改,并根据比较结果返回给用户最新内容,或者用 HTTP 响应码 304 告知用户,内容没有变化。
使用 HTTP 头进行并发处理
上文我们提到了使用条件请求控制缓存,其实我们还可以使用条件请求进行并发处理。 比如当用户 Alice 和 Bob 通过 REST 获取了一篇文档。Bob 阅读文档之后,通过 PUT 来修改文档;而此前几分钟,Alice 刚刚修改了这篇文档,于是 Bob 就在毫不知情的情况下不慎覆盖了 Alice 的修改。 通过在写操作中支持条件请求,我们可以更好的处理并发修改。用户在发出修改请求的同时,在 HTTP 请求中使用 If-Not-Modified-Since 或者 If-Match 头信息,把获取数据时得到的时间戳或者电子标签传给服务器;我们的程序通过与服务器当前内容的比较,就可以知道,这个修改请求是否是针对当前内容提出的。当服务器发现内容已经被其他用户修改过了,就不会执行修改请求,并返回 HTTP 响应码 412(未满足前提条件)给用户
5. 更多的需要注意的细节与技巧
批量更新
当用户需要更新多个资源的时候,你打算让开发者一次次的发送 HTTP 请求逐个更新吗?你可以考虑在设计 API 的时候允许客户同时创建或者更新多个资源。
REST 安全
除了使用固有的 HTTP 基本验证,你还可以考虑通过支持表单验证,LTPA 验证,Open ID 验证等方式,来满足更多的企业安全要求。
文档服务
是否由于 API 持续更新,使得客户端连接不同版本服务的时候疲于奔命?尝试着把你的 API 定义规范成 XML 文档,这样客户端很容易理解当前服务可以提供哪些功能,以及如何使用这些功能。