标准的 Delete
方法 必须(must) 返回 google.protobuf.Empty
来实现全局一致性。它还能够防止客户端依赖于在重试期间不可用的附加元数据。由于随着时间推移对于自定义方法, 对于自定义方法,它们 必须(must) 具备本身的 XxxResponse
消息,即便它们是空的,由于功能极可能随着时间的推移而增长,而且须要返回附加数据。node
表示范围的字段 应该(should) 使用符合命名约定的半开半闭区间 [start_xxx, end_xxx)
,例如 [start_key, end_key)
或 [start_time, end_time)
。C++ STL 和 Java 标准库常用半开半闭的语义。API 应该(should) 避免使用表示区间的其余方法,例如 (index, count)
或 [first, last]
。git
在面向资源的 API 中,资源结构由 API 定义。为了容许客户端向资源附加少许且简单的元数据(例如将一台虚拟机资源标记为数据库服务器),API 应该(should) 使用在 google.api.LabelDescriptor
中描述的资源标签设计模式。github
API 应该(should) 在资源定义中添加字段 map<string, string> labels
:web
message Book { string name = 1; map<string, string> labels = 2; }
若是一个 API 方法须要花费较长时间运行,能够将其设计成向客户端返回一个表示长时间运行的资源,客户端可经过这个资源来获取操做的执行进度并取得执行结果。Operation 定义了耗时操做的标准接口。不能(must not) 为 API 使用自定义的耗时操做接口以避免打破一致性。数据库
资源 必须(must) 做为响应消息直接返回,而且对资源操做的结果 应该(should) 反应在 API 中。例如:当建立资源时这个资源 应该(should) 显示在 LIST 和 GET 方法中,而且 应该(should) 指示出这个资源尚未准备好。若是方法不须要长期执行,当操做完成时 Operation.response
字段应该包含直接返回的消息。编程
即便结果集很小,可 LIST 的集合也 应该(should) 支持分页。设计模式
理由:尽管向已有的 API 添加分页功能从 API 的视角来看是纯粹的增长功能,但它实际会改变行为。不知道有分页功能的已有客户端会错误地将取到的第一页数据当成所有数据。api
为了在 List
方法中支持分页, API 应该(shall):缓存
在 List
方法的请求信息中定义一个 string
字段 page_token
。客户端经过这个字段来请求指定的某一页。
在 List
方法的请求信息中定义一个 int32
字段 page_size
。客户端经过这个字段来指定返回结果的最大数量。服务端能够进一步限制在单个页面中返回的最大结果数量。page_size
是 0 时,将由服务端决定返回结果的数量。
在 List
方法的响应信息中定义一个 string
字段 next_page_token
。这个字段表示取得下一页的页码。空字符串表示没有更多数据了。
为了取得下一页的结果,客户端 应该(shall) 将响应中的 next_page_token
传入下次的请求:
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse); message ListBooksRequest { string name = 1; int32 page_size = 2; string page_token = 3; } message ListBooksResponse { repeated Book books = 1; string next_page_token = 2; }
当客户端在 query 参数传入除 page token 以外的参数时,若是 query 参数与 page token 不一致,服务 必须(must) 拒绝此请求。
page token 的内容 应该(should) 是对 web 安全的 BASE64 编码后的 protocol buffer,这样就不会有兼容性问题。page token 中存在敏感信息时,应该(should) 将其加密。服务端 必须(must) 经过如下方法来防止经过篡改 page token 来获取敏感信息的问题:
根据后续请求指定 query 参数
在 page token 中仅引用服务端的状态
在 page token 中加密并签名 query 参数,而且在每次调用中对这些参数进行验证和鉴权
分页功能也 能够(may) 在响应中经过名为 total_size
类型为 int32
的字段来提供查询资源的总数量。
API 有时须要客户端对子集合进行 List/Search
操做。例如一个 API 有书架
集合,每一个书架有书
的集合,客户端想要在全部书架中搜索一本书。这种状况下推荐在子集合上使用标准的 List
,而且为父集合指定通配符 "-"
。例如:
GET https://library.googleapis.com/v1/shelves/-/books?filter=xxx
注意:使用 "-"
而非 "*"
是为了不 URL 转义。
有时子集合中的资源具备在其父集合内惟一的标识符,在这种状况下经过 Get
来取得某资源而不须要知道它的父集合多是有用的。在这种状况下,建议使用标准 Get
,并为资源惟一的全部父集合指定通配符 "-"
。例如:
GET https://library.googleapis.com/v1/shelves/-/books/{id}
响应 必须(must) 使用资源的带有父集合标识符的规范名称。例如上面的请求应该返回名称相似 shelves/shelf713/books/book8141
的资源,而不是 shelves/-/books/book8141
。
若是 API 方法容许客户端指定列表结果的排序顺序,请求消息中 应该(should) 包含以下字段:
string order_by = ...;
这个值 应该(should) 遵循 SQL 语法:用逗号分隔的字段列表。例如:"foo,bar"
。默认升序排列。应该(should) 给字段添加后缀 " desc"
来表示降序。例如:"foo desc,bar"
。
多余的空格能够忽略,"foo,bar desc"
和 " foo , bar desc "
是相等的。
若是 API 方法有反作用,而且须要仅验证请求而不产生反作用,请求消息 应该(should) 包含一个字段:
bool validate_only = ...;
当此字段设置为 true
时,服务端 必定不要(must not) 执行任何有反作用的操做,而是对请求进行校验。
校验成功时 必定(must) 要返回google.rpc.Code.OK
,而且使用相同请求信息的完整请求 不该该(should not) 返回 google.rpc.Code.INVALID_ARGUMENT
。注意,可能由于其余错误(好比 google.rpc.Code.ALREADY_EXISTS
或竞态条件)此请求仍是会失败。
对于网络 API,幂等是很重要的,由于当有网络异常时它们可以安全地进行重试。然而一些 API 并不容易实现幂等性,例如须要避免没必要要重复的建立资源操做。对于这类状况,请求信息 应该(should) 包含一个惟一 ID(例如 UUID),这样服务端可以经过此 ID 来检测重复,保证请求只被处理一次。
// 服务端用于检测重复请求的惟一 ID // 此字段应该命名为 `request_id` string request_id = ...;
由于客户端极可能没有接收到以前的响应,因此当检测到重复请求后,服务端 应该(should) 返回以前成功的响应。
每一个枚举定义 必须(must) 以 0
值开始,用于当枚举值没有明确指定时。API 必须(must) 在文档中说明如何处理 0
值。
若是有通用的默认行为,应该(should) 使用枚举值 0
。API 应该在文档中说明期待的行为。
若是没有通用的默认行为,枚举值 0
应该(should) 命名为 ENUM_TYPE_UNSPECIFIED
而且和错误 INVALID_ARGUMENT
一块儿使用。
enum Isolation { // 未指定 ISOLATION_UNSPECIFIED = 0; // 快照读。若是全部读写都不能在并发事务中逻辑地序列化,则会发生冲突 SERIALIZABLE = 1; // 快照读。并发事务向同一行写入时致使冲突 SNAPSHOT = 2; ... } // 当未指定时,服务器将使用 SNAPSHOT 或更高的隔离级别 Isolation level = 1;
一个惯用名称 能够(may) 用于 0
值,例如,google.rpc.Code.OK
是指定不存在错误的惯用方法。在这种状况下,OK
与枚举类型中的 UNSPECIFIED
在语义上是相等的。
在存在本质上合理和安全的默认状况下,能够(may) 使用 0
值。例如,在[资源视图]()枚举中 BASIC
是 0
值。
在某些 API 设计中,有必要为某些数据格式定义简单的语法,例如可接受的文本输入。为了在不一样 API 中提供一致的开发体验和减小学习曲线,API 设计者 必须(must) 使用 ISO 14977 扩展的 Backus-Naur 表格(EBNF)句法来定义这些语法。
Production = name "=" [ Expression ] ";" ; Expression = Alternative { "|" Alternative } ; Alternative = Term { Term } ; Term = name | TOKEN | Group | Option | Repetition ; Group = "(" Expression ")" ; Option = "[" Expression "]" ; Repetition = "{" Expression "}" ;
注意:TOKEN
表示在语法以外定义的终端。
在API 设计中,不该该(should not) 使用像 uint32
和 fixed32
这种无符号整型,这是由于一些重要的编程语言和系统(例如 Java, JavaScript 和 OpenAPI)不能很好地支持它们而且更容易致使溢出的问题。另外一个问题是,不一样的 API 极可能对同一个资源使用不匹配的有符号和无符号类型。
在大小和时间这种负数没有意义的类型中 能够(may) 使用且仅使用 -1
来表示特定的意义,例如到在文件结尾(EOF)、无穷的时间、无资源限额或未知的年纪。当这样使用负数时,必须(must) 在文档中明确说明以防止混淆。API 生成器也应该在文档中记录隐式默认值 0
表示的行为。
客户端有时只须要响应信息中的特定子集。一些 API 平台提供了对部分响应的原生支持。Google API 平台经过响应字段掩码来提供支持。对于任一 REST API 调用,有一个隐式的系统 query 参数 $fields
,它是 google.protobuf.FieldMask
的 JSON 表示。在返回给客户端以前,响应消息会被 $fields
字段过滤。此行为是在 API 平台自动执行的。
GET https://library.googleapis.com/v1/shelves?$fields=name
为了减小网络流量,容许客户端限制服务器在其响应中返回的资源的哪些部分是有用的,返回资源的视图而不是所有资源表示。API 中的资源视图是经过向请求添加参数来实现的,该参数容许客户端在响应中指定要接收资源的哪一个视图。
此参数:
应该(should) 是枚举类型
必须(must) 命名为 view
枚举中的每一个值定义了资源的哪部分(字段)在响应中会被返回。文档中 应该(should) 明确记录每一个 view
值会返回什么。
package google.example.library.v1; service Library { rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) { option (google.api.http) = { get: "/v1/{name=shelves/*}/books" } }; } enum BookView { // 响应中只包含做者、标题、ISBN 和惟一的图书 ID。这是默认值。 BASIC = 0; // 返回全部信息,包括书中的内容 FULL = 1; } message ListBooksRequest { string name = 1; // 指定返回图书资源的哪些部分 BookView view = 2; }
对应的 URL:
GET https://library.googleapis.com/v1/shelves/shelf1/books?view=BASIC
能够在 标准方法 一章中查看更多关于方法定义、请求和响应的内容。
ETag 是一个不透明的标识符,容许客户端进行条件请求。为了支持 ETag,API 应该(should) 在资源定义中包含一个字符串字段 etag
,它的语义 必须(must) 与 ETag的经常使用用法相匹配。一般,etag
包含由服务器计算出的资源指纹。更多详细信息,请参阅维基百科和 RFC 7232。
ETags 能够强验证或弱验证,其中弱验证的ETag 以 W /
为前缀。在这种状况下,强验证意味着具备相同 ETag 的两个资源具备相同的内容和相同的额外字段(Content-Type)。这意味着强验证的 ETag 容许缓存稍后组装的部分响应。
相反,具备相同弱验证 ETag 值的资源意味着这些表示在语义上是等效的,但不必定每字节都相同,所以不适合于字节范围请求的响应缓存。
// 强验证的 ETag(包含引号) "1a2f3e4d5b6c7c" // 弱验证的 ETag(包含前缀和引号) W/"1a2b3c4d5ef"
API 可能但愿将由客户端提供的字段和只由服务端在特定资源上返回的字段进行区分。对于仅输出的字段,必须(shall)记录字段属性。
请注意,若是客户端在请求中设置了仅输出(output only)字段,或者客户端使用仅输出字段指定了一个 google.protobuf.FieldMask
,则服务器 必须(must) 接受该请求而不能出错。这意味着服务器 必须(must) 忽略仅输出字段的存在及其任何指示。这个建议的缘由是由于客户端一般会将服务器返回的资源重用为另外一个请求的输入,例如一个获取到的 Book
将在 UPDATE 方法中被再次使用。若是要验证仅输出字段,客户端须要作清除输出字段的额外工做。
message Book { string name = 1; // 只用作输出 Timestamp create_time = 2; }