tommwq.work/aip

AIP-122 资源名字

· [tommwq@126.com]
编号 122
原文链接 https://google.aip.dev/122
状态 批准
创建日期 2019-01-26
更新日期 2019-01-26

大多数API会发布 资源 (主要是名词),用户可以创建、检索和操作这些资源。此外,资源是 具名的 :每个资源都有唯一的标识符,用户使用标识符引用资源。用户应当将这些名字作为资源的规范名字 存储

指南

API资源名字 必须 在API内唯一。(有关跨API引用资源的信息,请参考下面的完整资源名字部分。)

资源名字的格式遵守URI路径模式,但没有前导斜杠:

publishers/123/books/les-miserables users/vhugo1802

  • 资源名字的组成部分 应当 通常从集合标识符(例如: publishersbooksusers )和资源标识符(例如: 123les-miserablesvhugo1802 )之间选择。
  • 资源名字 必须 使用 / 字符来分隔资源名字的各个部分。
    • 资源名字的非终结部分 不得 包含 / 字符。
    • 资源名字的终结部分 不应 包含 / 字符。
  • 资源名字 应当 仅使用DNS名字中可用的字符,如RFC-1123定义。
    • 此外,资源标识 不应 使用大写字母。
    • 如果需要额外的字符,资源名字 不应 使用需要URL转义的字符或ASCII之外的字符。
    • 如果无法避免Unicode字符,资源名字 必须 以Unicode规范形式C存储(参考AIP-210)。
  • 资源 必须 发布一个包含资源名字的 name 域。
    • 资源 可以 将资源标识作为单独的域提供(例如 book_id )。这个域 必须 属于仅输出域行为类别。
    • 资源 可以 发布一个单独的、系统生成的唯一标识域(uid) 。这个域 必须 属于仅输出域行为类别。
    • 资源 不得 发布元组、自链接或其他形式的资源标识。
    • 所有标识域都 应当 是字符串。

注意: 此处描述的资源名字在单个API范围内使用(或者在从上下文可以确定拥有资源的API的情况下),并且只在范围内是唯一的。因此它们也被称为 相对资源名字 ,以区别于 完整资源名字 (下面讨论)。

集合标识符

资源名字中的集合标识符部分 必须 是资源名词的复数形式。(例如 Publisher 集合的资源名字是 publishers

  • 集合标识符 必须 是简洁的美式英语词汇。
  • 集合标识符 必须 使用驼峰( camelCase )命名方式。
  • 集合标识符 必须 以小写字母开头,只包含ASCII字母和数字( /[a-z][a-zA-Z0-9]*/ )。
  • 集合标识符 必须 是复数形式。
    • 在没有复数形式的情况下(如“info”),或者单数和复数形式相同的情况下(如“moose”),使用非复数形式(单数形式)是准确的。集合部分 不得 在这种情况下通过添加“s”来“捏造”新词汇(例如避免使用“infos”)。
  • 在任何给定的单一资源名字中,集合标识符 必须 是唯一的。(例如 people/xyz/people/abc 是无效的)

内嵌集合

如果资源名字包含多个层次结构,并且上级资源集合的名字用作下级资源名字的前缀,那么下级资源集合的名字 可以 省略前缀。例如嵌套在 users 下的 UserEvent 资源集合:

users/vhugo1802/userEvents/birthday-dinner-226

API 应当 使用较少冗余的形式:

users/vhugo1802/events/birthday-dinner-226

此时 消息资源类型 仍然是 UserEvent ;只有URI路径模式中集合和资源标识符被简化了。由于 资源类型 没有简化, singularplural 同样 不要简化

message UserEvent {
  option (google.api.resource) = {
    type: "example.googleapis.com/UserEvent"
    // Only the collection & resource identfiers in the `pattern` are shortened.
    pattern: "projects/{project}/users/{user}/events/{event}"
    singular: "userEvent"
    plural: "userEvents"
  };

  string name = 1;
}

注意: 希望这么做的API 必须 在所有 pattern 中,以及API中引用资源的任何地方统一遵守这个格式。

资源标识段

资源标识段在上级集合中标识资源。在资源名字 publishers/123/books/les-miserables 中, 123 是出版商的资源标识, les-miserables 是书籍的资源标识。

  • 如果资源标识是用户设定的,API 必须 在文档中记录所支持的格式。用户设定的资源标识 应当 符合RFC-1034;该标准要求名字使用字母、数字和连字符,首字符为字母,尾字符为字母或数字,且最大长度为63个字符。
    • 此外,用户设定的资源标识 应当 将字母限制为小写( ^[a-z]([a-z0-9-]{0,61}[a-z0-9])?$ )。
    • 不应 使用ASCII之外的字符;然而如果必须使用Unicode字符,API 必须 遵循AIP-210中的指南。
    • 不应 使用UUID(或任何在语法上看起来像UUID的值)作为用户设定标识符。
  • 如果资源标识不可由用户设置,API 应当 在文档中记录基本格式和长度限制(例如“最多63个字符”)。
  • 了解更多信息,参考CREATE标准方法。

资源标识别名

有时为常用查找模式提供资源标识别名是值得的。例如在其资源层次结构顶部带有 users 的API可能希望提供 users/me ,作为查询认证用户信息的快捷方式。

API 可以 为常用查找模式提供可编程的别名。但是,API返回的所有数据 必须 使用规范资源名字。

完整资源名字

在大多数情况下,资源名字仅在单个API中使用,或者用在所属API明确的上下文中(例如 string pubsub_topic )。

然而,有时服务需要引用任意API中的资源。此时服务 应当 使用 完整资源名字 ,这是一个无模式的URI,包含所属API服务名字,后接相对资源名字:

//library.googleapis.com/publishers/123/books/les-miserables
//calendar.googleapis.com/users/vhugo1802

注意: 完整资源名字 不应 用于所属API明确的跨API引用;只有当域引用多个API中的资源,并且可能存在歧义时使用。

资源URI

完整资源名字是一个无模式的URI,但与用于访问资源的完整URI略有不同。后者包括协议(HTTPS)、API版本和特定的服务端点

https://library.googleapis.com/v1/publishers/123/books/les-miserables https://calendar.googleapis.com/v3/users/vhugo1802

完整资源名字不包含版本,它被设计成在不同版本之间保持不变。即使API接口可能在主要版本之间发生变化,同一API的多个主要版本通常使用相同的底层数据。

注意: 完整资源名字与服务端点之间的按约定关联。特别是,一个服务可以有多个端点(例如区域化、MTLS和私有访问),完整资源名字在这些端点之间保持不变。

表示资源名字的域

在定义资源时,第一个域 应当 是资源名字,它 必须string 类型,并且 必须 称为 name 。消息 应当 包含 google.api.resource 注解,声明类型(更多信息请参考AIP-123)。

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

  // The resource name of the book.
  // Format: publishers/{publisher}/books/{book}
  string name = 1 [(google.api.field_behavior) = IDENTIFIER];

  // Other fields...
}

在定义查询或操作现有资源的方法(例如 GetBookArchiveBook )时,请求消息的第一个域 应当 是资源名字,它 必须string 类型, 必须 称为 name 。它还 应当 使用 google.api.resource_reference 注解,引用资源类型(AIP-123)。

// Request message for ArchiveBook
message ArchiveBookRequest {
  // The book to archive.
  // Format: publishers/{publisher}/books/{book}
  string name = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      type: "library.googleapis.com/Book"
    }];

  // Other fields...
}

注意: 除了用于这个目的,域 不得 称为 name 。对于其他场景,要么使用其他词汇,要么添加形容词前缀(例如 display_name )。

表示上级资源的域

在定义从集合中查询资源,或者向集合添加新资源的方法(例如 ListBooksCreateBook )时,请求消息的第一个域 应当string 类型, 应当 称为 parentparent 域还 应当 使用 google.api.resource_reference 注解,引用上级资源类型(API-123)。

// Request message for ListBooks.
message ListBooksRequest {
  // The publisher to list books from.
  // Format: publishers/{publisher_id}
  string parent = 1 [(google.api.resource_reference) = {
    type: "library.googleapis.com/Publisher"
  }];

  // Other fields (e.g. page_size, page_token, filter, etc.)...
}

如果有多个可能的上级类型, parent应当 使用 google.api.resource_reference 上的 child_type 键注解:

// Request message for ListBooks.
message ListBooksRequest {
  // The parent to list books from.
  // Format:
  //   - publishers/{publisher_id}
  //   - authors/{author_id}
  string parent = 1 [
    (google.api.field_behavior) = REQUIRED,
    (google.api.resource_reference) = {
      child_type: "library.googleapis.com/Book"
    }];

  // Other fields (e.g. page_size, page_token, filter, etc.)...
}

注意: 除非用于这个目的,域 不应 称为 parent 。对于其他场景,尽量使用同义词。

表示其他资源的域

当域表示另外的资源时,它 应当string 类型,值是另外资源的名字。域名字 应当 是对应消息名字的snake_case命名。

  • 域名字 可以 包括前导形容词(例如 string dusty_book )。
  • 除非可能引发歧义,域名字 不应 使用 _name 后缀,(例如 crypto_key_name )。
  • 表示另外资源的域 应当 提供 google.api.resource_reference 注解,引用对应资源类型。
  • 如果无法使用资源名字,同时标识组件也是必不可少的,域 应当 使用 _id 后缀(例如 shelf_id )。

除非 满足以下条件之一,域 不应 是实现对应资源的 message 类型:

  • API是内部专用的,具有紧凑的生命周期关系,并且其权限模型支持继承内嵌资源访问权限。
  • 内嵌资源是AIP-162修订模式的一部分。

资源引用示例:

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

  // Name of the book.
  // Format is `publishers/{publisher}/books/{book}`
  string name = 1 [(google.api.field_behavior) = IDENTIFIER];

  // The shelf where the book currently sits.
  // Format is `shelves/{shelf}`.
  string shelf = 2 [(google.api.resource_reference) = {
    type: "library.googleapis.com/Shelf"
  }];

  // Other fields...
}

进一步阅读

  • 关于资源名字随时间演进的问题,请参考AIP-180
  • 关于资源类型,请参考AIP-123

理由

使用名字代替标识

任何大型系统都有许多资源类型。要使用简单的标识来识别资源,实际上我们需要使用资源特定的元组来准确的标识它,例如 (bucket, object)(user, album, photo) 。这会产生几个问题:

  • 开发人员必须理解记忆这些不具名元组。
  • 传递元组通常比传递字符串更难。
  • 集中式基础设施(如日志和访问控制系统)无法理解特定用途元组。
  • 特定用途元组限制了API设计的灵活性,例如提供可重用的API接口。例如由于使用了灵活的资源名字,长时间运行的操作可以与许多其他API接口一起工作。

建立标准域 name

资源名字的概念并不是新的,它和统一资源标识符(URI)、统一资源定位符(URL)一起,在统一资源名字(URN)中正式确定。考虑到“name”一词在大部分情况下被过度使用,超出明确定义范围之外用法,会令开发人员感到困惑。因此在符合AIP标准的API中,域名字 name 被保留,避免资源名字混淆,强制其他可能被命名为“name”的域使用更具体的名字。

禁止资源嵌套

直接在资源中使用另一个资源的消息作为域的类型存在隐患,原因如下:

  • 让资源生命周期更复杂:如果上游资源被删除,下游资源中内嵌的资源引用会发生什么?数据保留和清理操作变得非常复杂。
  • 绕过权限:如果每个资源都有自己的权限集合,如果用户拥有读取下游资源权限,但没有读取上游资源权限,用户将无法看到完整资源。
  • 在每一方面都造成了资源紧耦合:更改任一资源的模式、权限或其他要求都会影响另外的资源,显著增加工作的复杂性。

按推荐方法,通过名字引用资源,可以消除上述复杂性,防止资源数重复,强制所属服务参与引用解析工作(通过标准方法),保证资源间的逻辑关注点隔离。

修订记录

  • 2024-10-15 : 添加关于使用 name 代替标识符作为域标识的理由。
  • 2024-06-14 : 明确内嵌集合使用的资源注释简化规则。
  • 2023-09-19 : 禁止集合标识符重复。
  • 2023-09-01 : 添加允许内嵌修订资源消息的条款。
  • 2023-08-10 : 明确禁止在资源中内嵌资源消息。
  • 2023-03-24 : 更正:完整资源名字包含服务名字,而非服务端点。
  • 2023-03-17 : 添加资源标识域 OUTPUT_ONLY 相关指南。
  • 2020-10-06 : 添加声明式友好指南,收紧字符集限制。
  • 2020-10-05 : 明确使用完整资源名字的场景。
  • 2020-05-19 : 明确要求资源 标识 (而非整个资源 名字 )避免大写字符。
  • 2020-04-27 : 收紧有效字符限制。
  • 2019-12-05 : 添加资源注释指南。
  • 2019-08-01 : 将示例“shelves”更改为“publishers”,提供更好的资源所有权示例。将最后一个示例从Pub/Sub示例改为通用的Book示例。
  • 2019-07-30 : 将内嵌集合简化建议从“可以”改为“应当”。