资源命名
在面向资源的API中,资源是命名实体,资源名称是资源的标识符。每个资源必须有其唯一的资源名称。资源名称由资源ID本身,父资源的ID和资源对应的API服务名称组成。在下文,我们将探讨资源ID和如何构建资源名称。
gRPC API应该使用无模式的URIs作为资源名称。它们通常遵循REST URL的惯例并且表现得更像网络文件路径。它们可以轻松的映射到REST URL上:细节请参考下一节标准方法。
资源组是一种特殊的资源,它包含一组相同类型的子资源。例如,一个目录是一组文件资源。组的资源ID被称为组ID。
资源名称使用组ID和资源ID分层组织,以斜杠(译者注:/,下同)分割。如果某个资源包含子资源,子资源的名称为:父资源名称,斜杠,子资源ID。
例1:一个存储服务有一组bucket
,每一个bucket有一组objects
:
API资源名称 | 组ID | 资源ID | 组ID | 资源ID |
---|---|---|---|---|
//storage.googleapis.com | /buckets | bucket-id | objects | object-id |
例2:一个邮件服务有一组user
,每个user有子资源settings
,settings还有子资源customFrom
API资源名称 | 组ID | 资源ID | 组ID | 资源ID |
---|---|---|---|---|
//mail.googleapis.com | /users | name@example.com | settings | customFrom |
API的提供者可以选择任何可接受的值作为资源和资源组的ID,只要它们在资源的层次结构中是唯一的即可。关于选择合适的资源ID和资源组ID,下文还有更多的参考。
只要资源名称的每段都不包含斜杠,通过分割资源名称可以获取独立的资源组ID和资源ID,比如name.split("/")[n]
。
资源全名
规则松散的URI由DNS兼容的API服务名称和资源路径组成。资源路径也称为相对资源名称。
“//library.googleapis.com/shelves/shelf1/books/book2”
API服务名称用于定位API服务端;它也可以是仅供内部的服务的伪DNS名称(译者注:API服务名可能用于外部调用,也可能只用于内部调用)。如果API服务名称在上下文中比较明显,相对资源名称则比较常用。
相对资源名称
没有前导“/”的URI路径(路径-无模式),标识API服务中的一个资源。比如:
“shelves/shelf1/books/book2”
资源ID
一个非空的URI片段(segment-nz-nc)标识父资源中的资源。比如上例。
资源名称中的资源ID后缀可以有不止一个URI片段。比如:
资源组ID | 资源ID |
---|---|
files | /source/py/parser.py |
API服务应当尽可能的使用URL友好的资源ID。资源ID必须清楚的描述,不管它是由客户端、服务器端还是其他方指定。
资源组ID
一个非空的URI片段(segment-nz-nc)标识父资源中的资源组,比如上例。
因为资源组ID经常出现在生成的客户端库里,它们必须符合以下要求:
- 必须是有效的C/C++标识符。
- 必须是驼峰命名的复数形式;首字母小写。
- 必须使用清晰简明的英文词语。
- 应当避免或者限定过于笼统的词语。比如,RowValue优于Value。没有限定的情况下应当避免以下词语:
- Element
- Entry
- Instance
- Item
- Object
- Resource
- Type
- Value
资源名称 vs URL
虽然完整的资源名称类似于普通URL,但他们不是一回事。单个资源可以由不同版本的API和不同API的协议暴露出来。完整资源名称没有指定API的协议和版本,在实际使用中,它必须被映射到特定的协议和API版本(译者注:完整资源名称)。
为了通过REST API使用资源的全名,必须在服务名称前添加HTTP协议,在资源路径前添加API主要版本号以及对资源路径进行URL转义,将其转换为REST URL。比如:
//这是一个日历事件资源名称
“//calendar.googleapis.com/users/john smith/events/123”
这是对应的HTTP URL
“https://calendar.googleapis.com/v3/users/john%20smith/events/123“
作为字符串的资源名称
Google API必须使用字符串表示资源名称,除非向后兼容性有问题。资源名称应当像正常文件路径那样处理,并且他们不支持%编码。
对于资源定义来说,资源名称的第一个字段应当是字符串字段,并被命名为Name。
注意:其他名称相关的字段应当具备避免混淆的命名,比如display\_name
, first\_name
, last\_name
, full\_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 {
//书的资源名称。格式必须是:"shelves/*/books/"
//比如:"shelves/shelf1/books/book2"。
string name = 1;
// ... 其他属性
}
message GetBookRequest {
//书的资源名称。"shelves/shelf1/books/book2"。
string name = 1;
}
message CreateBookRequest {
// 新建书的父资源的资源名称
// 比如"shelves/shelf1".
string parent = 1;
// 要创建的书籍资源,客户端绝不能设置‘Book.name’属性
Book book = 2;
}
注意:为了资源名称的统一,开头的斜杠绝不能让URL模板变量捕获。例如,必须使用URL模板”/v1/{name=shelves/*/books/*}”而不能使用”/v1{name=/shelves/*/books/*}”.