RESTful Web Service Cookbook 笔记
RESTful Web Service Cookbook 是一本简短、精炼的 RESTful 接口设计指南。这篇文章(笔记)用来记录这本书中提到的重点。
因为 RESTful 对后端开发来说实在太熟悉不过,所以我会省略掉那些习以为常的约定,只记录书中提到的、大多数开发者没有注意到的细节。
HTTP Method
GET
进行安全与幂等的信息获取。
POST
执行的目标是一个资源集合(工厂),而不是具体的 URI。
适用场景:
- 创建新的资源,把资源作为一个工厂。
- 通过一个控制器资源来修改一个或多个资源。
- 执行需要大数据输入(参数较多)的查询。
- 在其他 HTTP 方法看上去不合适时,执行不安全或非幂等的操作。
解决方案:
- 将一个已存在的资源标识为创建新资源的工厂。虽然您可以把任意资源用做工厂,但常见的做法是使用一个集合资源。
- 让客户端向工厂资源提交附有需要创建资源的表述的 POST 请求。通过可选支持的 Slug 头, 客户端可以向服务器建议一个名字,作为被创建资源的 URI 的一部分。
- 资源创建之后,返回响应码 201(Created),并在 Location 头中包含新创建资源的 URI。
- 如果响应正文包含了新创建资源的完整表述,那么在 Content-Location 头中包含新创建资源的 URI。
PUT
仅在客户端可以控制 URI 的构成时,才使用 PUT 方法创建新资源。(换句话说,PUT 也可以创建资源,但是仅限于客户端可以指定 URI)
确定资源对象的粒度
应该以适合客户端使用模式的方式来设计资源,而不是基于现有的数据库或对象模型。
- 可缓存性
- 减小修改频率
- 可变性——分离可变和不可变数据
如何设计复合资源?
复合资源降低了统一接口的可见性,因为它们的表述中包含了和其他资源相重叠的数据。
- 如果符合资源使用频率不高,可以考虑用缓存替代。
- 考虑网络开销,复合资源会不会降低服务端吞吐量,增大延时。
HTTP Body
以 JSON 格式的 Body 为例:
- 最好包含一个指向 self 的链接
- 如果分页,最好包含下一页的链接
- 如果分页,要指示集合的大小(总数)
- 如果查询对象是本地化的,添加一个属性来表示本地化内容的语言
{
"name": "John",
"id": "urn:example:user:1234",
"link": {
"rel": "self",
"href": "http://www.example.org/person/john"
},
"address": {
"id": "urn:example:address:4567",
"link": {
"rel": "self",
"href": "http://www.example.org/person/john/address"
}
}
}
HTTP Response
- 对于客户端错误,返回 4xx 状态码 + Date (错误发生的时间)。
- 对于服务端错误,返回 5xx 状态码 + Date (错误发生的时间)。
- Body 中要描述错误,如果有外部文档和链接可参考,在 Header 提供一个 Link 头或直接把链接写在 Body 里。
- 为了后期追踪或分析,在服务器上记录了错误日志,应该提供一个可以找到该错误的标识符或链接。
设计查询结构
设计查询请求
- 为了缓存和性能,尽量避免范围查询。解决方法包括:
- 使用预定义查询
- 也可以使用 HTTP Header: Range
- 避免使用通用语言(SQL、XPATH)的查询。
- 避免 URI 和数据存储方式的紧耦合(前端把后端当作数据库)。
- 对于参数较多,可以考虑使用 POST(因为 URI 长度有最大限制)
- POST 接口的缺点是丧失了缓存能力
- POST 请求是不可缓存的,所以 Cache-Control 和 Expires 头无济于事
- 解决缓存问题,可以让 POST 创建一个临时资源,把 link 返回前端,前端下次用 GET 获取该资源
设计查询响应结果
- 返回集合。添加合理的缓存过期头。
- 如果没有结果,应该返回空集合。