kratos 的proto http 插件——protoc-gen-go-http,对body以及query参数只能选择其一支持,不论其Method为何种。query、vars支持同时存在。

源码可查:go-kratos/kratos/cmd/protoc-gen-go-http/template.go

Google API 设计指南上对方法的设计有点不一样。

GET POST PUT DELETE
query (/hello?x=s) 支持 支持 支持 支持
vars (/hello/{name}) 支持 支持 支持 支持
body 支持 支持 支持 支持

body与query同时存在:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
service Auth {
rpc Register (RegisterReq) returns (RegisterReply) {
option (google.api.http) = {
post: "/api/v1/mdm/register"
body: "user_info"
};
}
}

message RegisterReq {
string username = 1;
string password = 2;
message UserInfo {
string info = 1;
}
UserInfo user_info = 3;
}


message RegisterReply {
int64 id = 1;
}

Header中解析数据

1
2
3
4
5
6
7
// client发上来的header保存在此处
transport.FromClientContext(ctx)

transport.FromServerContext(ctx)

和"google.golang.org/grpc/metadata"中的函数同:
FromIncomingContext、FromOutgoingContext

请求中的参数解析

从test文件的示例可以看出,github.com/go-kratos/kratos/encoding/form/form_test.go,解析URL中的嵌套数据的方法:

嵌套参数

1
2
3
4
5
6
7
message HelloRequest {
string name = 1;
message test {
string one = 1;
}
test ones = 2;
}

URL Query:

1
http://localhost:8000/helloworld?ones.one=1234

数组参数

1
2
3
Simples: []string{"3344", "5566"}

simples=3344&simples=5566

FieldMask参数

1
2
3
Field:     &fieldmaskpb.FieldMask{Paths: []string{"1", "2"}}

field=1,2

FieldMask借助库:

https://github.com/mennanov/fmutils

https://github.com/mennanov/fieldmask-utils

不过当前Kratos对query中的fieldmask会进行大写转换,导致其字段无法进行有效的Filter。我提了一个issue

fieldmask_utils是对字段名进行匹配,而不是tag名,而Kratos则会将Me ——> _me,而其字段为Me,那库则无法将其正常过滤,正常GRPC协议不会出现此转换,此转换应该是Kratos HTTP decode request时发生的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
import fieldmask_utils "github.com/mennanov/fieldmask-utils"

// 可以对fieldmask做一下中间拦截,也可以直接返回
func naming(s string) string {
if s == "foo" {
return "Foo"
}
return s
}

func main () {
var request UpdateUserRequest
userDst := &testproto.User{} // a struct to copy to
mask, _ := fieldmask_utils.MaskFromPaths(request.FieldMask.Paths, naming)
fieldmask_utils.StructToStruct(mask, request.User, userDst)
// Only the fields mentioned in the field mask will be copied to userDst, other fields are left intact
}

FieldMask用在response、request参数限制返回,以及指定参数的更新上。

Netflix API 设计实践: 使用FieldMask

request中指定paths,response根据paths mask参数,返回需要的字段。

Netflix API 设计实践(二): 使用FieldMask进行数据变更

1
2
3
4
5
6
7
8
9
10
11
12
13
14
message UpdateProductionRequest {
ProductionUpdateOperation update = 1;
google.protobuf.FieldMask update_mask = 2;
}

{
"update": {
"format": "test"
}
"update_mask": [
"format",
"schedule.planned_launch_date",
]
}

更新操作就会执行更新formatschedule.planned_launch_date两个字段,由于后者没有传值,变相的也就是将此字段置空。