tommwq.work/aip

AIP-144 重复域

· [tommwq@126.com]
编号 144
原文链接 https://google.aip.dev/144
状态 批准
创建日期 2020-03-19
更新日期 2020-03-19

在API中表示数据列表比看起来要复杂。用户常常需要原地修改列表,如果单个资源中存在较长数据序列,将对分页带来困难。

指南

资源 可以 在适当的地方使用重复域。

message Book {
  option (google.api.resource) = {
    type: "library.googleapis.com/Book"
    pattern: "publishers/{publisher}/books/{book}"
  };

  string name = 1 [(google.api.field_behavior) = IDENTIFIER];

  repeated string authors = 2;
}
  • 重复域 必须 使用复数形式的域名字。
    • 如果单数和复数形式相同(如“moose”、“info”), 必须 使用实际存在的词汇,而非试图创造新的复数形式。
  • 重复域 应当 存在强制上限,防止单个资源负载过大。一个好的经验法则是100个元素。
    • 如果重复数据可能超过上限,API 应当 使用子资源代替。
  • 重复域 不得 内联表示其他资源的主体。相反,消息 应当 提供关联资源的名字。

标量和消息

如果确认未来不需要额外数据,重复域 应当 使用标量类型(如 string ),使用消息类型会显著增加认知负担,产生复杂代码。

然而,如果未来可能需要额外数据,重复域 应当 主动使用消息而非标量,以避免平行的重复域。

更新策略

资源 可以 使用两种策略执行重复域更新操作:使用标准Update方法直接更新,或使用自定义的 AddRemove 方法。

标准 Update 方法存在一个重要限制:用户只能更新 完整 列表。域掩码无法处理重复域中的单个条目。这意味着用户必须读取资源,根据需要修改重复域值,再发送回去。这适用于很多场景,特别是重复域数据比较少(少于10个左右),且不存在竟态条件问题,或者可以使用ETags进行保护的时候。

注意 声明友好资源 必须 使用标准 Update 方法,不要引入 AddRemove 方法。如果声明式工具需要在忽略其他关系的情况下推理特定关系,考虑使用子资源。

如果需要原子修改操作,API 应当 使用动词 AddRemove 提供自定义方法:

注意 如果这两种策略限制性过强,考虑使用子资源。

rpc AddAuthor(AddAuthorRequest) returns (Book) {
  option (google.api.http) = {
    post: "/v1/{book=publishers/*/books/*}:addAuthor"
    body: "*"
  };
}

rpc RemoveAuthor(RemoveAuthorRequest) returns (Book) {
  option (google.api.http) = {
    post: "/v1/{book=publishers/*/books/*}:removeAuthor"
    body: "*"
  };
}
  • 添加或删除的数据 应当 使用基础类型(通常是 string )。
    • 对于具有主键的复杂数据结构,API 应当 使用包含 map 类型域的 Update 方法。
  • 接口名字 必须AddRemove 开头。其余部分 应当 是待添加域的单数形式。
  • 请求消息 必须 与接口名字一致,并带有 Request 后缀。
  • 应答消息 应当 是资源自身,应答包含有效上下文信息的除外。此时应答消息必须与接口名字一致,并带有 Response 后缀。
    • 当应答是资源自身时, 应当 包含资源的完整数据。
  • HTTP动词 必须 使用 POST ,这是自定义方法惯例。
  • HTTP URI 必须:add*:remove* 结尾,其中 * 是待添加或删除域的下划线风格单数名字。
  • 请求消息中,传递资源名字的域 应当 映射到URI路径。
    • HTTP变量 应当 是资源名字(如 book )而非 nameparent
    • 变量 应当 是URI路径中唯一的变量。
  • google.api.http 注解的 body 子句 应当"*"
  • 如果 Add 接口中添加的数据已经存在,方法 必须 返回 ALREADY_EXISTS 错误。
  • 如果 Remove 接口中删除的数据不存在,方法 必须 返回 NOT_FOUND 错误。

请求消息

message AddAuthorRequest {
  // The name of the book to add an author to.
  string book = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference).type = "library.googleapis.com/Book"
  ];

  string author = 2 [(google.api.field_behavior) = REQUIRED];
}

message RemoveAuthorRequest {
  // The name of the book to remove an author from.
  string book = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference).type = "library.googleapis.com/Book"
  ];

  string author = 2 [(google.api.field_behavior) = REQUIRED];
}
  • 必须 包含资源域。域 应当 是资源名字(如 book )而非 nameparent
  • 必须 包含待添加或删除值的域。 应当 是域的单数名字。
  • 请求消息 不得 包含任何其他必需域, 不应 包含其他可选域,本AIP或另外AIP要求的除外。

变更日志

  • 2022-06-02 更改后缀描述,消除多余的“-”。
  • 2020-10-17 建议在Add和Remove接口中返回资源自身,而非单独的应答类型。
  • 2020-10-17 添加了Add和Remove接口及请求指南。