AIP-158 分页
编号 | 158 |
---|---|
原文链接 | https://google.aip.dev/158 |
状态 | 批准 |
创建日期 | 2019-02-18 |
更新日期 | 2019-02-18 |
API通常需要提供数据集,最常见的是 List 标准方法。但集合大小往往是不受控制的,会随着时间增长,提高了查找时间和通过网络传输的应答大小。因此对集合进行分页是非常重要的。
指南
返回数据集的接口 必须 一开始就提供分页功能,因为向现存方法添加分页功能是无法向后兼容的变更。
// The request structure for listing books.
message ListBooksRequest {
// The parent, which owns this collection of books.
// Format: publishers/{publisher}
string parent = 1 [
(google.api.field_behavior) = REQUIRED,
(google.api.resource_reference) = {
child_type: "library.googleapis.com/Book"
}];
// The maximum number of books to return. The service may return fewer than
// this value.
// If unspecified, at most 50 books will be returned.
// The maximum value is 1000; values above 1000 will be coerced to 1000.
int32 page_size = 2;
// A page token, received from a previous `ListBooks` call.
// Provide this to retrieve the subsequent page.
//
// When paginating, all other parameters provided to `ListBooks` must match
// the call that provided the page token.
string page_token = 3;
}
// The response structure from listing books.
message ListBooksResponse {
// The books from the specified publisher.
repeated Book books = 1;
// A token that can be sent as `page_token` to retrieve the next page.
// If this field is omitted, there are no subsequent pages.
string next_page_token = 2;
}
- 返回集合的请求消息 应当 定义
int32 page_size
域,允许用户设定返回的最大结果数量。page_size
域 不得 是必需域。- 如果用户未设定
page_size
(或设定为0
),API选择合适的默认值。API 应当 在文档中记录默认值。API 不得 返回错误。 - 如果用户设定的
page_size
大于 API 允许的最大值,API 应当 将其强制设置为允许的最大页大小。 - 如果用户为
page_size
设定了负值,API 必须 返回INVALID_ARGUMENT
错误。 - API 可以 返回少于请求数量(包括零)的结果,即使未到达集合末尾。
- 集合的请求消息 应当 定义
string page_token
域,允许用户前进到集合的下一页。page_token
域 不得 是必需域。- 如果用户在后续分页请求中修改了
page_size
,服务 必须 遵守新的页大小。 - 用户要保持接口的所有其他参数不变;如果任何参数不同,API 应当 返回
INVALID_ARGUMENT
错误。
- 应答 不得 是流式应答。
- 返回集合的应答消息 应当 定义
string next_page_token
域,为用户提供用于检索下一页的页令牌。- 包含分页结果的域 应当 是消息的第一个域,编号为
1
。它 应当 是重复域,包含一组资源,构成一个结果页。 - 如果已到达集合末尾,
next_page_token
域 必须 为空。这是告诉用户“集合结束”的 唯一 方法。 - 如果未到达集合末尾(或API无法及时确认),API 必须 提供
next_page_token
。
- 包含分页结果的域 应当 是消息的第一个域,编号为
- 返回集合的应答消息 可以 提供
int32 total_size
域,为用户提供列表总项目数。- 总数 可以 是估计值(此时API 应当 在文档中明确记录)。
跳过部分结果
分页操作请求 可以 定义一个 int32 skip
域,允许用户跳过部分结果。
skip
值 必须 表示要待跳过的单个资源数量,而非页数量。
例如:
- 没有页令牌,且
skip
值为30
的请求,返回从第 31 个结果开始的单个页。 - 带有对应第51个结果的页令牌(因为前50个结果已在第1页返回),且
skip
值为30
的请求,返回从第 81 个结果开始的单个页。
如果提供的 skip
值使得游标移动到结果集末尾之后,应答 必须 是 200 OK
,且结果集为空,不再提供 next_page_token
。
不透明
API 提供的页令牌 必须 是不透明(符合URL要求)的字符串, 不得 让用户能够解析。如果用户能解析令牌, 他们真的会这么做 。这实际上让 API 分页的实现细节成为 API 界面的一部分,并且无法在不影响用户的情况下修改实现细节。
警告 对透明的页令牌进行 Base-64 编码 不是 有效的混淆机制。
如果页令牌不需要存储在数据库中,也不包含敏感数据,API 可以 定义一个包含所需数据的内部protocol buffer消息,发送消息的序列化结果(base-64 编码)作为混淆页令牌。
页令牌 必须 只用于提供分页过程继续位置的指示。 不得 提供对底层资源的任何形式的授权,授权 必须 像任何其他请求一样执行,无论是否存在页令牌。
页令牌失效
许多 API 在内部将页令牌存储在数据库中。此时API 可以 在发送页令牌后,经过一段合理时间,使页令牌失效,避免额外存储大量几乎无效的数据。这种行为不需要记录到文档中。
注意 不同API的合理时间可能不同,一个好的经验法则是3天。
向后兼容
向现存接口添加分页特性是无法向后兼容的变更。乍看很奇怪;向 proto 消息添加域通常是向后兼容的。然而添加分页在 行为上 是不兼容的。
考虑一个用户,集合中有 75 个资源。他已经编写并部署了代码。如果 API 后续添加分页域,将默认值设置为 50,他的代码将失效:代码原本取得所有资源,现在只取得前 50 个(并且代码还不知道如何进行分页)。即使 API 设置了很高的默认限制,例如 100,用户的集合可能会增长, 最后 代码还会失效。
此外,客户端库实现了自动分页,通常使用不同的方法签名区分分页接口和非分页接口。这意味着向未分页方法添加分页,将导致这些库产生破坏性更改。
因此,始终在返回集合的接口中 预先 添加分页很重要。真的很重要。以后再添加分页,无法避免不给现有用户带来麻烦。
警告 这也意味着,除了展示分页域外,接口的 实际实现 必须 使用有限的默认值。对于最初较小的集合,实现一个内存版本(取得的所有内容之后再分页)是合适的。
修订记录
- 2020-05-24 明确指出添加分页会破坏客户端库。
- 2020-05-13 添加跳过部分结果指导原则。
- 2020-08-24 明确指出应答不是流式应答。
- 2020-06-24 明确指出页大小对用户始终是可选的。
- 2019-02-12 添加关于分页域的指导原则。
- 2019-08-01 将示例从 “shelves” 改为 “publishers”,提供更好的资源所有权示例。
- 2019-07-19 将不透明性要求从“应该”改为“必须”。