grpc - 为什么自定义方法不应该使用 URL 来传输数据?
问题描述
TL,博士;在实现自定义方法时,“HTTP 配置 [...] 必须使用该body:*
子句,并且所有剩余的请求消息字段都应映射到 HTTP 请求正文。” . 为什么?
我对 Google 的API 设计指南有疑问 ,我正在尝试使用带有 Cloud Endpoints 的 gRPC来遵循该指南。
HttpRule
用于将HTTP /JSON 转码为 gRPC。HttpRule参考指出:
请注意,在正文映射中使用
*
时,不可能有 HTTP 参数,因为所有不受路径绑定的字段都以正文结尾。[...]的常见用法
*
是在根本不使用URL传输数据的自定义方法中。
...谷歌的自定义方法文档中也重复了一个观点,并在谷歌的 API Linter中得到了加强,
在映射中使用命名表示body
时,会留下一个定义明确的空间来以查询字符串参数的形式添加元数据;例如分页、链接、弃用警告、错误消息)。
service Messaging {
rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
option (google.api.http) = {
put: "/v1/messages/{message_id}"
// A named reference makes it possible to use querystring params
// and the HTTP body.
body: "data"
};
}
}
message UpdateMessageRequest {
message Data {
string foo = 1;
string bar = 2;
string baz = 3;
}
// mapped to the URL as querystring params
bool format = 1;
string revision = 2;
// mapped to the body
Data data = 3;
}
这允许 HTTP PUT 请求/v1/messages/123456?format=true&revision=2
与正文
foo="I am foo"
bar="I am bar"
baz="I am baz"
由于映射绑定body
到 type UpdateMessageRequest.Data
,其余字段最终在查询字符串中。这是标准方法中使用的方法,但不是自定义) 方法。
自定义方法必须映射body
到*
. 具有自定义方法的相同 API 将是
service Messaging {
rpc UpdateMessage(UpdateMessageRequest) returns (Message) {
option (google.api.http) = {
put: "/v1/messages/{message_id}"
// Every field not bound by the path template should be
// mapped to the request body.
body: "*"
};
}
}
message UpdateMessageRequest {
message Data {
string foo = 1;
string bar = 2;
string baz = 3;
}
// mapped to the body
bool format = 1;
string revision = 2;
Data data = 3;
}
如果在标准和自定义) 方法中使用相同的元数据,则必须将其添加为查询字符串参数,或放置在正文中。
例如,一个 Angular 应用程序会使用HttpParams
// standard method
const params = new HttpParams().append('format', true).append('revision', 2);
const request = {
foo: "I am foo",
bar: "I am bar",
baz: "I am baz",
}
this.http.post<Document>(url, request, {params});
但是,自定义方法需要客户端将所有内容放在正文中:
// custom method
const request = {
format: true,
revision: 2,
data: {
foo: "I am foo",
bar: "I am bar",
baz: "I am baz",
},
}
this.http.post<Document>(url, request);
问:这是什么原因?
解决方案
好问题。
作为参考,我编写了关于这个主题的 AIP以及 lint 规则,并且我也是您所引用的设计指南的当前维护者。
首先,我会提到我们的最新指南(上面链接)特别说明了应该而不是必须。换句话说,大多数时候做正确的事,但也可能有例外。gRPC 转码实现中的任何内容都不会阻止您使用不同的方法body
——我们告诉您使用*
自定义方法,但我们不会对做其他事情设置任何技术障碍。
我可以想到几个很好的“例外情况”,其中一个主体*
可能是有意义的。第一个是自定义方法,它以标准方法之一为模型,但出于某种原因应该是自定义的。第二个是如果自定义方法接受了完整的资源,并且想要将主体设置为该资源。这将使该方法与Create
and一致Update
,这显然对 API 的用户有价值。
如果你有一个明确的论据来使用其他东西作为主体(特别是如果那是资源本身),请务必使用不同的主体并告诉 linter 保持安静。我们写“应该”是有原因的。
您还问:为什么我们首先提出该建议?
有几个原因。最大的一点是,上述的例外情况很少见。我在内部进行了数百次 API 审查,但我真的想不出一个(这并不是说它们不存在)。大多数时候,对用户来说最好的事情是让请求消息镜像 HTTP 有效负载。
另一个原因是一个关键限制:将特定字段指定为正文会限制您可以在该字段之外添加的内容,因为查询字符串在类型(仅原语)和数量(URI 长度约束)中可以表示的内容受到限制。因为更改body
后者会构成重大更改,所以这会在某种程度上束缚您的手。显然,这对您的用例可能没问题,但请务必注意。
无论如何,我希望这会有所帮助——哦,感谢您使用我的东西。:-)
推荐阅读
- python - "with suppress" not working anymore in my Python 3 code?
- sql - 返回 sql 之间的月份
- android - 允许用户在不使用自动旋转的情况下左右横向
- node.js - 正确创建/解析 JSON
- clojure - 如何使用管道上的传感器创建多个输出?
- iis - 在 IIS 中使用 serilog 进行文件记录
- javascript - 同步随机变量
- java - 如何在 xpages 中读取 Java 中的 css 文件
- python - 最后一行的值会覆盖 for 循环中的其他行
- python - QHBoxLayout(大小,调整大小,移动)