编号 | 134 |
---|---|
原文链接 | AIP-134: Standard methods: Update |
状态 | 批准 |
创建日期 | 2019-01-24 |
更新日期 | 2022-06-02 |
REST API通常向资源URI(如 /v1/publishers/{publisher}/books/{book}
)发出 PATCH
或 PUT
请求,更新资源。
面向资源设计(AIP-121)提供Update方法(类似REST PATCH行为),遵循这一模式。这些接口接受代表资源的URI,返回资源。
指南
API通常 应当 为资源提供Update方法,除非对用户没实际意义。Update方法的目的是修改资源,而不引发副作用。
Update方法使用以下模式:
rpc UpdateBook(UpdateBookRequest) returns (Book) {
option (google.api.http) = {
patch: "/v1/{book.name=publishers/*/books/*}"
body: "book"
};
option (google.api.method_signature) = "book,update_mask";
}
- 接口名字 必须 以单词Update开头,其余部分 应当 是资源消息名字的单数形式。
- 请求消息 必须 与接口名字一致,并带有
Request
后缀。 - 应答消息必须是资源本身。(不存在
UpdateBookResponse
。)- 应答 应当 包含完整资源数据, 必须 包含客户端发送的全部域,并在域掩码中包含这些域。除非它们是仅输入域(参考AIP-203)。
- 如果Update接口是耗时更新,应答消息 必须
google.longrunning.Operation
,解析为资源自身。
- 方法 应当 支持更新部分资源,对应HTTP动词是
PATCH
。- 如果方法仅支持完全资源替换,HTTP动词 可以 是
PUT
。然而非常不建议这样做,这导致向资源添加新域的变更无法向后兼容。
- 如果方法仅支持完全资源替换,HTTP动词 可以 是
- 资源的
name
域 应当 映射到URI路径。{resource}.name
域 应当 是URI路径中唯一的变量。
google.api.http
注解中 必须 包含body
键, 必须 映射到请求消息中的资源域。- 所有其他域 应当 映射到URI查询参数。
- 应当 只有一个
google.api.method_signature
注解,值为"{resource},update_mask"
。 - 如果API在管理平面上运行,操作应该具有强一致性:更新操作完成时,所有用户可设定值和资源的存在性 必须 达到稳定状态,读取资源状态将返回一致的应答。
#+begin_quote
注意:* 与其他四个标准方法不同,这里的URI路径引用了示例中的嵌套域( book.name
)。如果资源域存在单词分隔符,使用 snake_case
。
#+end_quote
请求消息
Update方法实现了常见的请求消息模式:
message UpdateBookRequest {
// The book to update.
//
// The book's `name` field is used to identify the book to update.
// Format: publishers/{publisher}/books/{book}
Book book = 1 [(google.api.field_behavior) = REQUIRED];
// The list of fields to update.
google.protobuf.FieldMask update_mask = 2;
}
- 请求消息 必须 包含表示资源的域。
- 域 必须 映射到
PATCH
主体。 - 域 应当 注释为必需域。
- 资源消息 必须 包含
name
域。 name
域 必须 标识需要更新的资源的类型。
- 域 必须 映射到
- 如果允许更新部分资源, 必须 包含域掩码。 必须 是
google.protobuf.FieldMask
类型, 必须 称为update_mask
。- 域掩码中包含的域对应于欲更新资源(而非请求消息)。
- 域 必须 是可选的,服务 必须 将缺失的域掩码视为等同于包含了所有域的域掩码。
- 更新掩码 必须 支持特殊值
*
,表示完全替换资源(等同于PUT
)。- API生产者在向资源添加新的可变域时,需要注意消费者在不知道新可变域的情况下是如何处理通配符(
*
)的。同样地,消费者只有风险可控的条件下才使用通配符。通常而言,最安全的做法是明确指定要更新的域,而非通配符(*
)。
- API生产者在向资源添加新的可变域时,需要注意消费者在不知道新可变域的情况下是如何处理通配符(
- 请求消息 不得 包含任何其他必需域, 不应 包含其他可选域,除非本AIP或其他AIP有要求。
副作用
通常,Update方法的目标是更新资源中的数据。Update方法 不应 引发其他副作用。相反,副作用 应当 通过自定义方法触发。
特别的,这意味着在Update方法中,状态域不可以直接修改。
PATCH
和 PUT
概要: Google API通常只使用HTTP动词
PATCH
,不支持PUT
请求。
Google统一使用 PATCH
,因为在变更稳定API时提升了向后兼容性。常常需要向现有资源添加新域,但在使用 PUT
时,这会成为破坏性变更。
为了说明这一点,考虑对 Book
资源的 PUT
请求:
PUT /v1/publishers/123/books/456
{"title": "Mary Poppins", "author": "P.L. Travers"}
接下来考虑资源后来增加了一个新域(这里添加了 rating
):
message Book {
string title = 1;
string author = 2;
// Subsequently added to v1 in place...
int32 rating = 3;
}
如果一个图书资源设置了评分,然后执行了旧版本的 PUT
请求,将清除图书评分信息。本质上 PUT
请求无意中清除了数据,因为旧版本不知道这个域。
耗时更新
某些资源在更新时需要的时间比常规API应答时间更长。此时API应使用耗时操作(AIP-151)模式:
rpc UpdateBook(UpdateBookRequest) returns (google.longrunning.Operation) {
option (google.api.http) = {
patch: "/v1/{book.name=publishers/*/books/*}"
body: "book"
};
option (google.longrunning.operation_info) = {
response_type: "Book"
metadata_type: "OperationMetadata"
};
}
- 应答类型 必须 设置为资源(如果接口不是耗时操作,返回类型会是什么)。
- 必须 设定
response_type
和metadata_type
域。
#+begin_quote
注意:* 声明友好资源(AIP-128) 应当 使用耗时更新。
#+end_quote
Create或Update
如果服务使用客户端分配的资源名字,Update方法 可以 提供 bool allow_missing
域,让方法允许用户更新一个不存在的资源(在处理过程中创建资源):
message UpdateBookRequest {
// The book to update.
//
// The book's `name` field is used to identify the book to be updated.
// Format: publishers/{publisher}/books/{book}
Book book = 1 [(google.api.field_behavior) = REQUIRED];
// The list of fields to be updated.
google.protobuf.FieldMask update_mask = 2;
// If set to true, and the book is not found, a new book will be created.
// In this situation, `update_mask` is ignored.
bool allow_missing = 3;
}
更具体地说, allow_missing
标志引发以下行为:
- 如果方法在一个不存在的资源上调用,创建资源。设定所有请求中包含的域,无论是否出现在域掩码中。
- 如果缺少必需域或域值无效,返回
INVALID_ARGUMENT
错误。
- 如果缺少必需域或域值无效,返回
- 如果方法在已存在的资源上调用,并且所有域值一致,返回未修改的现存资源。
- 如果方法在已存在的资源上调用,只更新域掩码中声明的域。
用户 必须 拥有更新权限才能调用Update,即使将 allow_missing
设置为 true
。对于希望防止用户通过Update方法创建资源的API, 应当 使用IAM(身份和访问管理)条件。
Etags
API有时需要允许用户发送Update请求,请求需要保证应用到最新数据上(常见的使用场景是为了检测和避免竞态条件)。需要启用这个功能的资源要包含 string etag
域,包含一个由服务器计算的、代表资源内容的密值。
此时资源 应当 包含 string etag
域:
message Book {
option (google.api.resource) = {
type: "library.googleapis.com/Book"
pattern: "publishers/{publisher}/books/{book}"
};
// The resource name of the book.
// Format: publishers/{publisher}/books/{book}
string name = 1 [(google.api.field_behavior) = IDENTIFIER];
// The title of the book.
// Example: "Mary Poppins"
string title = 2;
// The author of the book.
// Example: "P.L. Travers"
string author = 3;
// The etag for this book.
// If this is provided on update, it must match the server's etag.
string etag = 4;
}
etag
域 可以 是必须域或可选域。如果设定了 etag
域,只有当值与服务器计算结果匹配时,请求才能成功。否则 必须 返回 ABORTED
错误。请求中的 update_mask
域不会影响 etag
域的行为,因为不是一个 需要 更新的域。
高成本域
API有时会遇到这种情况:资源中某些域的计算成本很高,或者根本无法返回可靠值。
这可能发生在以下几种场景:
- 资源中可能有一些域的计算成本非常高,并且通常在Update请求中对客户没用。
- 单一资源有时用来表示来自多个底层(最终一致的)数据源的数据的汇总。此时无法返回那些未修改域的准确信息。
在这种情况下,API 可以 只返回已更新域,省略其他。如果这样做, 应当 用文档记录这种行为。
错误
参考错误,特别是何时使用PERMISSION_DENIED和NOT_FOUND错误。
此外,如果用户确实拥有适当权限,但请求的资源不存在,服务 必须 返回 NOT_FOUND
(HTTP 404)错误, allow_missing
设定为 true
时除外。
修订记录
- 2024-12-03 添加使用完整替换域掩码的注意事项。
- 2024-03-14 将
update_mask
域的可选行为指南改为 必须 。 - 2023-08-26 添加一致性要求。
- 2023-07-17 将
update_mask
域名字指南改为 必须 。 - 2022-11-04 将错误指南汇总到AIP-193。
- 2022-06-02 修改后缀描述,消除多余的“-”。
- 2021-11-04 修改设定
allow_missing
时的权限检查。 - 2021-07-08 添加资源未找到场景的错误指南。
- 2021-03-05 将
etag
错误从FAILED_PRECONDITION
(即HTTP 400)改为ABORTED
(409)。 - 2020-10-06 添加声明友好资源指南。
- 2020-10-06 添加
allow_missing
指南。 - 2020-08-14 添加权限拒绝场景错误指南。
- 2020-06-08 添加返回完整资源数据的指南。
- 2019-10-18 添加注释指南。
- 2019-09-10 添加耗时操作AIP链接(AIP-151)。
- 2019-08-01 将示例从“shelves”改为“publishers”,提供更好的资源所有权示例。
- 2019-06-10 添加耗时更新指南。
- 2019-05-29 添加禁止在标准方法中使用任意域的明确禁令。