在面向资源的 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(假设任何段都不包含正斜杠)。
完整资源名称
无传输协议的 URI 由 DNS 兼容的 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_name
或 title
或 full_name
。
问:如何生成和解析资源名称?
资源名称的行为与文件路径类似。您可以使用 printf()
根据资源 ID 生成资源名称。您可以使用 split()
将资源名称解析为资源 ID。请注意,某些尾随资源 ID 可以具有多个由 /
分隔的 URI 段,例如文件路径。