资源名称

在面向资源的 API 中,“资源”是被命名的实体,“资源名称”是它们的标识符。每个资源都必须具有自己唯一的资源名称。 资源名称由资源自身的 ID、任何父资源的 ID 及其 API 服务名称组成。在下文中,我们将查看资源 ID 以及如何构建资源名称。

gRPC API 应使用无传输协议的 URI 作为资源名称。它们通常遵循 REST 网址规则,其行为与网络文件路径非常相似。它们可以轻松映射到 REST 网址:如需了解详情,请参阅标准方法部分。

“集合”是一种特殊的资源,包含相同类型的子资源列表。例如,目录是文件资源的集合。集合的资源 ID 称为集合 ID。

资源名称由集合 ID 和资源 ID 构成,按分层方式组织并以正斜杠分隔。如果资源包含子资源,则子资源的名称由父资源名称后跟子资源的 ID 组成,也以正斜杠分隔。

示例 1:存储服务具有一组 buckets,其中每个存储桶都有一组 objects

API 服务名称 集合 ID 资源 ID 集合 ID 资源 ID
//storage.googleapis.com /buckets /bucket-id /objects /object-id

示例 2:电子邮件服务具有一组 users。每个用户都有一个 settings 子资源,而 settings 子资源拥有包括 customFrom 在内的许多其他子资源:

API 服务名称 集合 ID 资源 ID 资源 ID 资源 ID
//mail.googleapis.com /users /name@example.com /settings /customFrom

API 生产者可以为资源和集合 ID 选择任何可接受的值,只要它们在资源层次结构中是唯一的。您可以在下文中找到有关选择适当的资源和集合 ID 的更多准则。

通过拆分资源名称(例如 name.split("/")[n]),可以获得单个集合 ID 和资源 ID(假设任何段都不包含正斜杠)。

完整资源名称

无传输协议的 URIDNS 兼容的 API 服务名称和资源路径组成。资源路径也称为“相对资源名称”。 例如:

"//library.googleapis.com/shelves/shelf1/books/book2"

API 服务名称供客户端定位 API 服务端点;它可以是仅限内部服务的虚构 DNS 名称。如果 API 服务名称在上下文中很明显,则通常使用相对资源名称。

相对资源名称

开头没有“/”的 URI 路径 (path-noscheme)。它标识 API 服务中的资源。例如:

"shelves/shelf1/books/book2"

资源 ID

资源 ID 通常包含一个或多个用于标识非父资源中资源的非空 URI 段 (segment-nz-nc),请参见上文的示例。资源名称中的非末尾资源 ID 必须只有 1 个网址段,而资源名称末尾的资源 ID 可以具有多个 URI 段。例如:

集合 ID 资源 ID
文件 source/py/parser.py

API 服务应该尽可能使用网址友好的资源 ID。 资源 ID 必须被清楚地记录,无论它们是由客户端、服务器还是其中一个分配的。例如,文件名通常由客户端分配,而电子邮件消息 ID 通常由服务器分配。

集合 ID

标识其父资源中集合资源的非空 URI 段 (segment-nz-nc),请参见上文的示例。

由于集合 ID 通常出现在生成的客户端库中,因此它们必须符合以下要求:

  • 必须是有效的 C/C++ 标识符。
  • 必须是复数形式的首字母小写驼峰体。如果该词语没有合适的复数形式,例如“evidence(证据)”和“weather(天气)”,则应该使用单数形式。
  • 必须使用简明扼要的英文词语。
  • 应该避免过于笼统的词语,或对其进行限定后再使用。例如,rowValues 优先于 values应该避免在不加以限定的情况下使用以下词语:
    • elements
    • entries
    • instances
    • items
    • objects
    • resources
    • types
    • values

资源名称和网址

虽然完整的资源名称类似于普通网址,但两者并不相同。单个资源可以由不同的 API 版本、API 协议或 API 网络端点公开。完整资源名称未指明此类信息,因此在实际使用中必须将其映射到特定的 API 版本和 API 协议。

要通过 REST API 使用完整资源名称,必须将其转换为 REST 网址,实现方法为在服务名称之前添加 HTTPS 传输协议、在资源路径之前添加 API 主要版本以及对资源路径进行网址转义。例如:

// This is a calendar event resource name.
"//calendar.googleapis.com/users/john smith/events/123"

// This is the corresponding HTTP URL.
"https://calendar.googleapis.com/v3/users/john%20smith/events/123"

资源名称为字符串

除非存在向后兼容问题,否则 Google API 必须使用纯字符串来表示资源名称。资源名称应该像普通文件路径一样处理。当资源名称在不同组件之间传递时,必须将它视为原子值,并且不得有任何数据损失。

对于资源定义,第一个字段应该是资源名称的字符串字段,并且应该称为 name

例如:

service LibraryService {
  rpc GetBook(GetBookRequest) returns (Book) {
    option (google.api.http) = {
      get: "/v1/{name=shelves/*/books/*}"
    };
  };
  rpc CreateBook(CreateBookRequest) returns (Book) {
    option (google.api.http) = {
      post: "/v1/{parent=shelves/*}/books"
      body: "book"
    };
  };
}

message Book {
  // Resource name of the book. It must have the format of "shelves/*/books/*".
  // For example: "shelves/shelf1/books/book2".
  string name = 1;

  // ... other properties
}

message GetBookRequest {
  // Resource name of a book. For example: "shelves/shelf1/books/book2".
  string name = 1;
}

message CreateBookRequest {
  // Resource name of the parent resource where to create the book.
  // For example: "shelves/shelf1".
  string parent = 1;
  // The Book resource to be created. Client must not set the `Book.name` field.
  Book book = 2;
}

注意:为了保证资源名称的一致性,前导正斜杠不得被任何网址模板变量捕获。例如,必须使用网址模板 "/v1/{name=shelves/*/books/*}",而非 "/v1{name=/shelves/*/books/*}"

问题

问:为什么不使用资源 ID 来标识资源?

任何大型系统都有很多种资源。在使用资源 ID 来标识资源的时候,我们实际上是使用特定于资源的元组来标识资源,例如 (bucket, object)(user, album, photo)。这会带来几个主要问题:

  • 开发者必须了解并记住这些匿名元组。
  • 传递元组通常比传递字符串更难。
  • 集中式基础架构(例如日志记录和访问控制系统)不理解专用元组。
  • 专用元组限制了 API 设计的灵活性,例如提供可重复使用的 API 接口。例如,长时间运行的操作可以与许多其他 API 接口一起使用,因为它们使用灵活的资源名称。

问:为什么资源名称字段名为 name,而不是 id

资源名称字段以资源“名称”的概念命名。整体而言,我们发现 name 的概念会让开发者感到困惑。例如,文件名实际上只是名称还是完整路径?通过预留标准字段 name,开发者不得不选择更合适的词语,例如 display_nametitlefull_name

问:如何生成和解析资源名称?

资源名称的行为与文件路径类似。您可以使用 printf() 根据资源 ID 生成资源名称。您可以使用 split() 将资源名称解析为资源 ID。请注意,某些尾随资源 ID 可以具有多个由 / 分隔的 URI 段,例如文件路径。