Skip to content
目录

谷歌API设计指南

设计模式

Google REST API(也被称为REST方法)的 标准方法ListGetCreateUpdateDelete

设计流程

  • 确定一个API提供什么类型的资源
  • 确定资源之间的关系
  • 根据资源类型和关系确定资源命名方案
  • 明确资源 schema
  • 给资源添加最少的方法集

资源

面向资源的 API 通常被建模为一个资源层次结构。其中每一个节点可以是一个 简单资源 也可以是 一组资源。为了方便,它们通常被称为 资源 或者 资源组

尽管 REST API 和存储系统有概念上的一致性,但具有面向资源的 API 的服务不一定是数据库。

方法

面向资源的 API 的关键特性是强调资源(数据模型)和运行在资源上的方法(功能)。典型的面向资源的 API 通过少量方法的暴露大量资源。

空响应

标准的 Delete 方法应该返回 google.protobuf.Empty,除非它正在执行“软”删除,在这种情况下,该方法应该返回状态已更新的资源,以指示正在进行删除。

自定义方法应该有自己的 XxxResponse 消息(即使为空),因为它们的功能很可能会随着时间的推移而增长并需要返回其他数据。

表示范围

表示范围的字段应该使用半开区间和命名惯例 [start_xxx, end_xxx),例如 [start_key, end_key)[start_time, end_time)。通常 C ++ STL 库和 Java 标准库会使用半开区间语义。API 应该避免使用其他表示范围的方式,例如 (index, count)[first, last]

资源标签

在面向资源的 API 中,资源架构由 API 定义。要让客户端将少量简单元数据附加到资源(例如,将虚拟机资源标记为数据库服务器),API 应该 使用 google.api.LabelDescriptor 中描述的资源标签设计模式。

为此,API 设计应该map<string, string> labels 字段添加到资源定义中。

protobuf
message Book {
  string name = 1;
  map<string, string> labels = 2;
}

长时间运行的操作

如果某个 API 方法通常需要很长时间才能完成,您可以通过适当设计,让其向客户端返回“长时间运行的操作”资源,客户端可以使用该资源来跟踪进度和接收结果。 Operation 定义了一个标准接口来使用长时间运行的操作。 各个 API 不得为长时间运行的操作定义自己的接口,以避免不一致性。

操作资源必须作为响应消息直接返回,操作的任何直接后果都应该反映在 API 中。例如,在创建资源时,即便资源表明其尚未准备就绪,该资源也应该出现在 LIST 和 GET 方法中。操作完成后,如果方法并未长时间运行,则 Operation.response 字段应包含本应直接返回的消息。

操作可以使用 Operation.metadata 字段提供有关其进度的信息。即使初始实现没有填充 metadata 字段,API 也应该为此元数据定义消息。

列表分页

可列表集合应该支持分页,即使结果通常很小。

说明:如果某个 API 从一开始就不支持分页,稍后再支持它就比较麻烦,因为添加分页会破坏 API 的行为。 不知道 API 正在使用分页的客户端可能会错误地认为他们收到了完整的结果,而实际上只收到了第一页。

为了在 List 方法中支持分页(在多个页面中返回列表结果),API 应该

  • List 方法的请求消息中定义 string 字段 page_token。客户端使用此字段请求列表结果的特定页面。
  • List 方法的请求消息中定义 int32 字段 page_size。客户端使用此字段指定服务器返回的最大结果数。服务器可以进一步限制单个页面中返回的最大结果数。如果 page_size0,则服务器将决定要返回的结果数。
  • List 方法的响应消息中定义 string 字段 next_page_token。此字段表示用于检索下一页结果的分页令牌。如果值为 "",则表示请求没有进一步的结果。

要检索下一页结果,客户端应该在后续的 List 方法调用中(在请求消息的 page_token 字段中)传递响应的 next_page_token 值:

protobuf
rpc ListBooks(ListBooksRequest) returns (ListBooksResponse);

message ListBooksRequest {
  string parent = 1;
  int32 page_size = 2;
  string page_token = 3;
}

message ListBooksResponse {
  repeated Book books = 1;
  string next_page_token = 2;
}

当客户端传入除页面令牌之外的查询参数时,如果查询参数与页面令牌不一致,则服务必须使请求失败。

页面令牌内容应该是可在网址中安全使用的 base64 编码的协议缓冲区。 这使得内容可以在避免兼容性问题的情况下演变。如果页面令牌包含潜在的敏感信息,则应该对该信息进行加密。服务必须通过以下方法之一防止篡改页面令牌导致数据意外暴露:

  • 要求在后续请求中重新指定查询参数。
  • 仅在页面令牌中引用服务器端会话状态。
  • 加密并签署页面令牌中的查询参数,并在每次调用时重新验证并重新授权这些参数。

分页的实现也可以提供名为 total_sizeint32 字段中的项目总数。

列出子集合

有时,API 需要让客户跨子集执行 List/Search 操作。例如,“API 图书馆”有一组书架,每个书架都有一系列书籍,而客户希望在所有书架上搜索某一本书。在这种情况下,建议在子集合上使用标准 List,并为父集合指定通配符集合 ID "-"。对于“API 图书馆”示例,我们可以使用以下 REST API 请求:

http
GET https://library.googleapis.com/v1/shelves/-/books?filter=xxx

注意:选择 "-" 而不是 "*" 的原因是为了避免需要进行 URL 转义。

从子集合中获取唯一资源

有时子集合中的资源具有在其父集合中唯一的标识符。此时,在不知道哪个父集合包含它的情况下使用 Get 检索该资源可能很有用。在这种情况下,建议对资源使用标准 Get,并为资源在其中是唯一的所有父集合指定通配符集合 ID "-"。例如,在 API 图书馆中,如果书籍在所有书架上的所有书籍中都是唯一的,我们可以使用以下 REST API 请求:

http
GET https://library.googleapis.com/v1/shelves/-/books/{id}

响应此调用的资源名称必须使用资源的规范名称,并使用实际的父集合标识符而不是每个父集合都使用 "-"。例如,上面的请求应返回名称为 shelves/shelf713/books/book8141(而不是 shelves/-/books/book8141)的资源。

排序顺序

如果 API 方法允许客户端指定列表结果的排序顺序,则请求消息应该包含一个字段:

protobuf
string order_by = ...;

字符串值应该遵循 SQL 语法:逗号分隔的字段列表。例如:"foo,bar"。默认排序顺序为升序。要将字段指定为降序,应该将后缀 " desc" 附加到字段名称中。例如:"foo desc,bar"

语法中的冗余空格字符是无关紧要的。 "foo,bar desc"" foo , bar desc " 是等效的。

提交验证请求

如果 API 方法有副作用,并且需要验证请求而不导致此类副作用,则请求消息应该包含一个字段:

protobuf
bool validate_only = ...;

如果此字段设置为 true,则服务器不得执行任何副作用,仅执行与完整请求一致的特定于实现的验证。

如果验证成功,则必须返回 google.rpc.Code.OK,并且任何使用相同请求消息的完整请求不得返回 google.rpc.Code.INVALID_ARGUMENT。请注意,由于其他错误(例如 google.rpc.Code.ALREADY_EXISTS 或争用情况),请求可能仍然会失败。

请求重复

对于网络 API,幂等 API 方法是首选,因为它们可以在网络故障后安全地重试。但是,某些 API 方法不能轻易为幂等(例如创建资源),并且需要避免不必要的重复。对于此类用例,请求消息包含唯一 ID(如 UUID),服务器将使用该 ID 检测重复并确保请求仅被处理一次。

protobuf
// A unique request ID for server to detect duplicated requests.
// This field **should** be named as `request_id`.
string request_id = ...;

如果检测到重复请求,则服务器应该返回先前成功请求的响应,因为客户端很可能未收到先前的响应。

枚举默认值

每个枚举定义必须0 值条目开头,当未明确指定枚举值时,使用该条目。API 必须记录如何处理 0 值。

如果存在共同的默认行为,则应该使用枚举值 0,并且 API 应记录预期的行为。

如果没有共同的默认行为,则枚举值 0 应该被命名为 ENUM_TYPE_UNSPECIFIED 并且在使用时应该使用错误 INVALID_ARGUMENT 拒绝。

protobuf
enum Isolation {
  // Not specified.
  ISOLATION_UNSPECIFIED = 0;
  // Reads from a snapshot. Collisions occur if all reads and writes cannot be
  // logically serialized with concurrent transactions.
  SERIALIZABLE = 1;
  // Reads from a snapshot. Collisions occur if concurrent transactions write
  // to the same rows.
  SNAPSHOT = 2;
  ...
}

// When unspecified, the server will use an isolation level of SNAPSHOT or
// better.
Isolation level = 1;

惯用名称可以用于 0 值。例如,google.rpc.Code.OK 是指定缺少错误代码的惯用方法。在这种情况下,在枚举类型的上下文中,OK 在语义上等同于 UNSPECIFIED

如果存在本质上合理且安全的默认值,则该值可以用于“0”值。例如,BASIC资源视图枚举中的“0”值。

语法规则

在 API 设计中,通常需要为某些数据格式定义简单的语法,例如可接受的文本输入。为了在所有 API 中提供一致的开发者体验并减少学习曲线,API 设计人员必须使用以下扩展巴科斯范式(Extended Backus-Naur Form,简写为“EBNF”)语法的变体来定义这样的语法:

protobuf
Production  = name "=" [ Expression ] ";" ;
Expression  = Alternative { "|" Alternative } ;
Alternative = Term { Term } ;
Term        = name | TOKEN | Group | Option | Repetition ;
Group       = "(" Expression ")" ;
Option      = "[" Expression "]" ;
Repetition  = "{" Expression "}" ;

注意TOKEN 表示在语法之外定义的终端符号。

整数类型

在 API 设计中,不应该使用 uint32fixed32 等无符号整数类型,因为某些重要的编程语言和系统(如 Java,JavaScript 和 OpenAPI)不太支持它们。并且它们更有可能导致溢出错误。另一个问题是,不同的 API 很可能会对同一事件使用不匹配的有符号和无符号类型。

当有符号整数类型用于负值无意义的事物(例如大小或超时)时,值 -1 (且仅有 -1可以用于表示特殊含义,例如文件结尾 (EOF)、无限超时、无限制配额限制或未知年龄。必须明确记录此类用法以避免混淆。如果隐式默认值 0 的行为不是非常明显,API 提供方也应对其进行记录。

部分响应

有时,API 客户端只需要响应消息中的特定数据子集。为了支持此类用例,某些 API 平台为部分响应提供原生支持。Google API Platform 通过响应字段掩码来支持它。任何 REST API 调用都有一个隐式系统查询参数 $fields,它是 google.protobuf.FieldMask 值的 JSON 表示形式。在发送回客户端之前,响应消息将由 $fields 过滤。API 平台会自动为所有 API 方法处理此逻辑。

protobuf
GET https://library.googleapis.com/v1/shelves?$fields=name

资源视图

为了减少网络流量,有时可允许客户端限制服务器应在其响应中返回的资源部分,即返回资源视图而不是完整的资源表示形式。API 中的资源视图支持是通过向方法请求添加一个参数来实现的,该参数允许客户端指定希望在响应中接收的资源视图。

该参数具有以下特点:

  • 应该enum 类型
  • 必须命名为 view

枚举的每个值定义将在服务器的响应中返回资源的哪些部分(哪些字段)。为每个 view 值返回的具体内容是由实现定义的,应该在 API 文档中指定。

protobuf
package google.example.library.v1;

service Library {
  rpc ListBooks(ListBooksRequest) returns (ListBooksResponse) {
    option (google.api.http) = {
      get: "/v1/{name=shelves/*}/books"
    }
  };
}

enum BookView {
  // Not specified, equivalent to BASIC.
  BOOK_VIEW_UNSPECIFIED = 0;

  // Server responses only include author, title, ISBN and unique book ID.
  // The default value.
  BASIC = 1;

  // Full representation of the book is returned in server responses,
  // including contents of the book.
  FULL = 2;
}

message ListBooksRequest {
  string name = 1;

  // Specifies which parts of the book resource should be returned
  // in the response.
  BookView view = 2;
}

此构造将映射到网址中,例如:

http
GET https://library.googleapis.com/v1/shelves/shelf1/books?view=BASIC

ETag

ETag 是一个不透明标识符,允许客户端发出条件请求。 为了支持 ETag,API 应该在资源定义中包含字符串字段 etag,并且其语义必须符合 ETag 的常见用法。通常,etag 包含服务器计算的资源的指纹。如需了解更多详情,请参阅 WikipediaRFC 7232

ETag 可以被强验证或弱验证,其中弱验证的 ETag 以 W/ 为前缀。在本上下文中,强验证意味着具有相同 ETag 的两个资源具有逐字节相同的内容和相同的额外字段(即,内容-类型)。这意味着强验证的 ETag 允许缓存部分响应并在稍后组合。

相反,具有相同的弱验证 ETag 值的资源意味着表示法在语义上是等效的,但不一定逐字节相同,因此不适合字节范围请求的响应缓存。

例如:

protobuf
// This is a strong ETag, including the quotes.
"1a2f3e4d5b6c7c"
// This is a weak ETag, including the prefix and quotes.
W/"1a2b3c4d5ef"

请牢记,引号是 ETag 值的一部分并且必须存在,以符合 RFC 7232。这意味着 ETag 的 JSON 表示法最终会对引号进行转义。例如,ETag 在 JSON 资源正文中表示为:

protobuf
// Strong
{ "etag": "\"1a2f3e4d5b6c7c\"", "name": "...", ... }
// Weak
{ "etag": "W/\"1a2b3c4d5ef\"", "name": "...", ... }

ETag 中允许的字符摘要:

  • 仅限可打印的 ASCII
    • RFC 2732 允许的非 ASCII 字符,但对开发者不太友好
  • 不能有空格
  • 除上述位置外,不能有双引号
  • 遵从 RFC 7232 的推荐,避免使用反斜杠,以防止在转义时出现混淆

输出字段

API 可能希望区分客户端提供的作为输入的字段,以及由服务器返回的仅在特定资源上输出的字段。对于仅限输出的字段,应该记录字段属性。

请注意,如果在请求中设置了或在 google.protobuf.FieldMask 中包括了仅输出字段,则服务器必须接受请求并且不出现错误。服务器必须忽略仅限输出字段的存在及任何提示。此建议的原因是因为客户端经常将服务器返回的资源作为另一个请求的输入重新使用,例如,检索到的 Book 稍后将在 UPDATE 方法中被重新使用。如果对仅限输出字段进行验证,则会导致客户端需要额外清除仅限输出字段。

protobuf
message Book {
  string name = 1;
  // Output only.
  Timestamp create_time = 2;
}

单例资源

当只有一个资源实例存在于其父资源中(如果没有父资源,则在 API 中)时,可以使用单例资源。

单例资源必须省略标准的 CreateDelete 方法;在创建或删除父资源时即隐式创建或删除了单例资源(如果没有父资源,则单例资源隐式存在)。必须使用标准的 GetUpdate 方法,以及任意适合您的用例的自定义方法访问该资源。

例如,具有 User 资源的 API 可以将每个用户的设置公开为 Settings 单例。

protobuf
rpc GetSettings(GetSettingsRequest) returns (Settings) {
  option (google.api.http) = {
    get: "/v1/{name=users/*/settings}"
  };
}

rpc UpdateSettings(UpdateSettingsRequest) returns (Settings) {
  option (google.api.http) = {
    patch: "/v1/{settings.name=users/*/settings}"
    body: "settings"
  };
}

[...]

message Settings {
  string name = 1;
  // Settings fields omitted.
}

message GetSettingsRequest {
  string name = 1;
}

message UpdateSettingsRequest {
  Settings settings = 1;
  // Field mask to support partial updates.
  FieldMask update_mask = 2;
}

流式半关闭

对于任何双向或客户端流传输 API,服务器应该依赖 RPC 系统提供的、客户端发起的半关闭来完成客户端流。无需定义显式完成消息。

客户端需要在半关闭之前发送的任何信息都必须定义为请求消息的一部分。

网域范围名称

网域范围名称是以 DNS 域名为前缀的实体名称,旨在防止名称发生冲突。当不同的组织以分散的方式定义其实体名称时,这种设计模式很有用。其语法类似于没有架构的 URI。

网域范围名称广泛用于 Google API 和 Kubernetes API,例如:

  • Protobuf Any 类型的表示形式:type.googleapis.com/google.protobuf.Any
  • Stackdriver 指标类型:compute.googleapis.com/instance/cpu/utilization
  • 标签键:cloud.googleapis.com/location
  • Kubernetes API 版本:networking.k8s.io/v1
  • x-kubernetes-group-version-kind OpenAPI 扩展程序中的 kind 字段。

布尔值与枚举与字符串

在设计 API 方法时,您通常会为特定功能(例如启用跟踪或停用缓存)提供一组选择。实现此目的的常用方法是引入 boolenumstring 类型的请求字段。对于给定用例,要使用哪种正确的类型通常不是很明显。推荐的选项如下:

  • 如果我们希望获得固定的设计并且有意不想扩展该功能,请使用 bool 类型。例如 bool enable_tracingbool enable_pretty_print
  • 如果我们希望获得灵活的设计,但不希望该设计频繁更改,请使用 enum 类型。一般的经验法则是枚举定义每年仅更改一次或更少。例如 enum TlsVersionenum HttpVersion
  • 如果我们采用开放式设计或者可以根据外部标准频繁更改设计,请使用 string 类型。必须明确记录支持的值。例如:

数据保留

在设计 API 服务时,数据保留是服务可靠性相当重要的部分。通常,用户数据会被软件错误或人为错误误删。没有数据保留和相应的取消删除功能,一个简单的错误就可能对业务造成灾难性的影响。

通常,我们建议 API 服务采用以下数据保留政策:

  • 对于用户元数据、用户设置和其他重要信息,应保留 30 天的数据。例如,监控指标、项目元数据和服务定义。
  • 对于大量用户内容,应保留 7 天的数据。例如,二进制 blob 和数据库表。
  • 对于暂时性状态或费用昂贵的存储服务,如果可行,应保留 1 天的数据。例如,Memcache 实例和 Redis 服务器。

在数据保留期限期间,可以删除数据而不会丢失数据。如果免费提供数据保留的成本很高,则服务可以提供付费的数据保留。

大型载荷

联网 API 通常依赖于多个网络层作为其数据路径。大多数网络层对请求和响应大小有硬性限制。32 MB 是很多系统中常用的限制。

在设计处理大于 10 MB 的载荷的 API 方法时,我们应该谨慎选择合适的策略,以确保易用性和满足未来增长的需求。对于 Google API,我们建议使用流式传输或媒体上传/下载来处理大型载荷。如使用流式传输,服务器会逐步地同步处理大量数据,例如 Cloud Spanner API。如使用媒体,大量数据会流经大型存储系统(如 Google Cloud Storage),服务器可以异步处理数据,例如 Google Drive API。

命名规则

服务名

语法上有效的 DNS 名称,可以解析为一个或多个网络地址。如:cs.lmcw.art channelservice

软件包名

.proto 文件中声明的软件包名称应该与产品名称和服务名称保持一致。

软件包名称应该使用单数组件名称,以避免混合使用单数和复数组件名称。

软件包名称不能使用下划线。

进行版本控制的 API 的软件包名称必须以此版本结尾。

集合 ID

应采用复数和 lowerCamelCase(小驼峰式命名法)格式,并遵循美式英语拼写和语义。例如:eventschildrendeletedEvents

接口名称

指在 .proto 文件中定义 service 时使用的名称。

接口名称应该使用直观的名词,例如 Calendar 或 Blob。该名称不得与编程语言及其运行时库(如 File)中的成熟概念相冲突。

在极少数情况下,接口名称会与 API 中的其他名称相冲突,此时应该使用后缀(例如 Api 或 Service)来消除歧义。

方法名称

服务可以在其 IDL 规范中定义一个或多个远程过程调用 (RPC) 方法,这些方法需与集合和资源上的方法对应。方法名称应采用大驼峰式命名格式并遵循 VerbNoun 的命名惯例,其中 Noun(名词)通常是资源类型。

动词名词方法名称请求消息响应消息
ListBookListBooksListBooksRequestListBooksResponse
GetBookGetBookGetBookRequestBook
CreateBookCreateBookCreateBookRequestBook
UpdateBookUpdateBookUpdateBookRequestBook
RenameBookRenameBookRenameBookRequestRenameBookResponse
DeleteBookDeleteBookDeleteBookRequestgoogle.protobuf.Empty

方法名称的动词部分 应该 使用用于要求或命令的祈使语气,而不是用于提问的陈述语气。

例如,要求 API 创建一本书,这显然是 CreateBook(祈使语气),但是询问 API 关于图书发行商的状态可能会使用陈述语气,例如 IsBookPublisherApprovedNeedsPublisherApproval。若要在此类情况下继续使用祈使语气,请使用“check”(CheckBookPublisherApproved) 和“validate”(ValidateBookPublisher) 等命令

方法名称 不应 包含介词(例如“For”、“With”、“At”、“To”)。通常,带有介词的方法名称表示正在使用新方法,应将一个字段添加到现有方法中,或者该方法应使用不同的动词。

例如,如果 CreateBook 消息已存在且您正在考虑添加 CreateBookFromDictation,请考虑使用 TranscribeBook 方法。

消息名称

消息名称 应该 简洁明了。避免不必要或多余的字词。如果不存在无形容词的相应消息,则通常可以省略形容词。例如,如果没有非共享代理设置,则 SharedProxySettings 中的 Shared 是多余的。

消息名称 不应 包含介词(例如“With”、“For”)。通常,带有介词的消息名称可以通过消息上的可选字段来更好地表示。

请求和响应消息

RPC 方法的请求和响应消息 应该 分别以带有后缀 RequestResponse 的方法名称命名,除非方法请求或响应类型为以下类型:

  • 一条空消息(使用 google.protobuf.Empty
  • 一个资源类型
  • 一个表示操作的资源

这通常适用于在标准方法 GetCreateUpdateDelete 中使用的请求或响应。

枚举名称

枚举类型 必须 使用 UpperCamelCase 格式的名称。

枚举值 必须 使用 CAPITALIZED_NAMES_WITH_UNDERSCORES 格式。每个枚举值 必须 以分号(而不是逗号)结尾。第一个值应该命名为 ENUM_TYPE_UNSPECIFIED,因为在枚举值未明确指定时系统会返回此值。

protobuf
enum FooBar {
  // The first value represents the default and must be == 0.
  FOO_BAR_UNSPECIFIED = 0;
  FIRST_VALUE = 1;
  SECOND_VALUE = 2;
}

字段名称

.proto 文件中的字段定义 必须 使用 lower_case_underscore_separated_names 格式。这些名称将映射到每种编程语言的生成代码中的原生命名惯例。

字段名称 不应 包含介词(例如“for”、“during”、“at”),例如:

  • reason_for_error 应该改成 error_reason
  • cpu_usage_at_time_of_failure 应该改成 failure_time_cpu_usage

字段名称不应使用后置形容词(名词后面的修饰符),例如:

  • items_collected 应该改成 collected_items
  • objects_imported 应该改成 imported_objects

时间和时间段

要表示一个与任何时区或日历无关的时间点,应该 使用 google.protobuf.Timestamp,并且字段名称 应该time(例如 start_timeend_time)结尾。

如果时间指向一个活动,则字段名称 应该 采用 verb_time 的形式,例如 create_timeupdate_time。请勿使用动词的过去时态,例如 created_timelast_updated_time

要表示与任何日历和概念(如“天”或“月”)无关的两个时间点之间的时间跨度,应该 使用 google.protobuf.Duration

protobuf
message FlightRecord {
  google.protobuf.Timestamp takeoff_time = 1;
  google.protobuf.Duration flight_duration = 2;
}

如果由于历史性或兼容性原因(包括系统时间、时长、推迟和延迟),您必须使用整数类型表示与时间相关的字段,那么字段名称 必须 采用以下格式:

protobuf
xxx_{time|duration|delay|latency}_{seconds|millis|micros|nanos}

message Email {
  int64 send_time_millis = 1;
  int64 receive_time_millis = 2;
}

如果由于历史性或兼容性原因,您必须使用字符串类型表示时间戳,则字段名称 不应该 包含任何单位后缀。字符串表示形式 应该 使用 RFC 3339 格式,例如“2014-07-30T10:43:17Z”。

日期和时间

对于与时区和时段无关的日期,应该 使用 google.type.Date,并且该名称应具有后缀 _date。如果日期必须表示为字符串,则应采用 ISO 8601 日期格式 YYYY-MM-DD,例如 2014-07-30。

对于与时区和日期无关的时间,应该 使用 google.type.TimeOfDay,并且该名称应具有后缀 _time。如果时间必须表示为字符串,则应采用 ISO 8601 24 小时制格式 HH:MM:SS[.FFF],例如 14:55:01.672。

protobuf
message StoreOpening {
  google.type.Date opening_date = 1;
  google.type.TimeOfDay opening_time = 2;
}

数量

由整数类型表示的数量 必须 包含度量单位。

protobuf
xxx_{bytes|width_pixels|meters}

如果数量是条目计数,则该字段 应该 具有后缀 _count,例如 node_count

列表过滤器字段

如果 API 支持对 List 方法返回的资源进行过滤,则包含过滤器表达式的字段 应该 命名为 filter。例如:

protobuf
message ListBooksRequest {
  // The parent resource name.
  string parent = 1;

  // The filter expression.
  string filter = 2;
}

列表响应

List 方法的响应消息(包含资源列表)中的字段名称 必须 是资源名称本身的复数形式。例如,CalendarApi.ListEvents() 方法 **必须 **为返回的资源列表定义一个响应消息ListEventsResponse,其中包含一个名为 events 的重复字段。

protobuf
service CalendarApi {
  rpc ListEvents(ListEventsRequest) returns (ListEventsResponse) {
    option (google.api.http) = {
      get: "/v3/{parent=calendars/*}/events";
    };
  }
}

message ListEventsRequest {
  string parent = 1;
  int32 page_size = 2;
  string page_token = 3;
}

message ListEventsResponse {
  repeated Event events = 1;
  string next_page_token = 2;
}

驼峰式命名法

除字段名称和枚举值外,.proto 文件中的所有定义都 必须使用由 Google Java 样式 定义的 UpperCamelCase 格式的名称。

名称缩写

对于软件开发者熟知的名称缩写,例如 configspec应该在 API 定义中使用这些缩写,而非完整名称。这将使源代码易于读写。而在正式文档中,应该使用完整名称。示例:

  • config (configuration)
  • id (identifier)
  • spec (specification)
  • stats (statistics)