为什么自定义方法不应该使用 URL 来传输数据?
Why shouldn't custom methods use the URL for transferring data?
TL,DR; 执行custom methods时,"the HTTP configuration [...] must use the body:*
clause and all remaining request message fields shall map to the HTTP request body."。 为什么?
我对 Google 的 API Design Guide which I'm attempting to follow with gRPC with Cloud Endpoints 有疑问。
HttpRule
用于transcode HTTP/JSON to gRPC. The HttpRule reference状态:
Note that when using *
in the body mapping, it is not possible to have
HTTP parameters, as all fields not bound by the path end in the body.
[...] The common usage of *
is in custom methods which don't use the
URL at all for transferring data.
...在 Google 的 Custom Methods documentation and reinforced with Google's API Linter,
中也重复了一个观点
当在 body
映射中使用 命名表示 时,有一个定义明确的 space 留下来以查询字符串参数的形式添加元数据;例如。用于分页、链接、弃用警告、错误消息)。
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;
}
这允许向 /v1/messages/123456?format=true&revision=2
发送带有主体
的 HTTP PUT 请求
foo="I am foo"
bar="I am bar"
baz="I am baz"
由于映射将 body
绑定到类型 UpdateMessageRequest.Data
,其余字段最终出现在查询字符串中。这是 standard methods, but not with custom) 方法中使用的方法。
自定义方法必须将 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;
}
如果 相同的元数据 在两个 standard and custom) 方法中使用,它必须作为查询字符串参数添加,或者放在正文中。
例如,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);
问题:这是什么原因?
好问题。
作为参考,我写了 the AIP on this topic 以及 lint 规则,我也是您引用的设计指南的当前维护者。
首先,我要提到我们的最新指南(上面的链接)明确指出 应该 而不是 必须 。换句话说,大多数时候这样做是正确的,但也可能有例外。 the gRPC transcoding implementation 中的任何内容都不会阻止您使用不同的 body
-- 我们告诉您使用 *
自定义方法,但我们不会对做其他事情设置任何技术障碍。
我能想到一些不错的 "exception cases",其中 *
以外的机构可能有意义。第一个是自定义方法,它以标准方法之一为模型,但出于某种原因应该是自定义的。第二种情况是自定义方法接受完整资源,并希望将正文设置为该资源。这将使该方法与 Create
和 Update
一致,这显然对 API 的用户有价值。
如果你有一个明确的论据来使用其他东西作为主体(特别是如果那个东西是资源本身),无论如何,使用不同的主体并告诉 linter 安静。我们写 "should" 是有原因的。
您还问过:为什么我们首先会有这个建议?
有几个原因。最大的一个是上述例外情况很少见。我在内部进行了数百次 API 评论,但实际上我想不出其中之一(这并不是说它们不存在)。大多数时候,对用户来说最好的事情是请求消息反映 HTTP 负载。
另一个原因是关键限制:将特定字段指定为正文会限制您可以在该字段外部 添加的内容,因为查询字符串在它们可以表示的内容上受到限制在类型(只是原语)和数量(URI 长度限制)方面。因为稍后更改 body
构成了重大更改,所以这在一定程度上束缚了您的手脚。显然,这可能适合您的用例,但请务必注意。
无论如何,我希望能有所帮助 -- 哦,感谢您使用我的东西。 :-)
TL,DR; 执行custom methods时,"the HTTP configuration [...] must use the body:*
clause and all remaining request message fields shall map to the HTTP request body."。 为什么?
我对 Google 的 API Design Guide which I'm attempting to follow with gRPC with Cloud Endpoints 有疑问。
HttpRule
用于transcode HTTP/JSON to gRPC. The HttpRule reference状态:
Note that when using
*
in the body mapping, it is not possible to have HTTP parameters, as all fields not bound by the path end in the body.[...] The common usage of
*
is in custom methods which don't use the URL at all for transferring data.
...在 Google 的 Custom Methods documentation and reinforced with Google's API Linter,
中也重复了一个观点当在 body
映射中使用 命名表示 时,有一个定义明确的 space 留下来以查询字符串参数的形式添加元数据;例如。用于分页、链接、弃用警告、错误消息)。
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;
}
这允许向 /v1/messages/123456?format=true&revision=2
发送带有主体
foo="I am foo"
bar="I am bar"
baz="I am baz"
由于映射将 body
绑定到类型 UpdateMessageRequest.Data
,其余字段最终出现在查询字符串中。这是 standard methods, but not with custom) 方法中使用的方法。
自定义方法必须将 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;
}
如果 相同的元数据 在两个 standard and custom) 方法中使用,它必须作为查询字符串参数添加,或者放在正文中。
例如,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);
问题:这是什么原因?
好问题。
作为参考,我写了 the AIP on this topic 以及 lint 规则,我也是您引用的设计指南的当前维护者。
首先,我要提到我们的最新指南(上面的链接)明确指出 应该 而不是 必须 。换句话说,大多数时候这样做是正确的,但也可能有例外。 the gRPC transcoding implementation 中的任何内容都不会阻止您使用不同的 body
-- 我们告诉您使用 *
自定义方法,但我们不会对做其他事情设置任何技术障碍。
我能想到一些不错的 "exception cases",其中 *
以外的机构可能有意义。第一个是自定义方法,它以标准方法之一为模型,但出于某种原因应该是自定义的。第二种情况是自定义方法接受完整资源,并希望将正文设置为该资源。这将使该方法与 Create
和 Update
一致,这显然对 API 的用户有价值。
如果你有一个明确的论据来使用其他东西作为主体(特别是如果那个东西是资源本身),无论如何,使用不同的主体并告诉 linter 安静。我们写 "should" 是有原因的。
您还问过:为什么我们首先会有这个建议?
有几个原因。最大的一个是上述例外情况很少见。我在内部进行了数百次 API 评论,但实际上我想不出其中之一(这并不是说它们不存在)。大多数时候,对用户来说最好的事情是请求消息反映 HTTP 负载。
另一个原因是关键限制:将特定字段指定为正文会限制您可以在该字段外部 添加的内容,因为查询字符串在它们可以表示的内容上受到限制在类型(只是原语)和数量(URI 长度限制)方面。因为稍后更改 body
构成了重大更改,所以这在一定程度上束缚了您的手脚。显然,这可能适合您的用例,但请务必注意。
无论如何,我希望能有所帮助 -- 哦,感谢您使用我的东西。 :-)