tangs b030ce89ca openbilibili 5 rokov pred
..
codegenerator b030ce89ca openbilibili 5 rokov pred
examples b030ce89ca openbilibili 5 rokov pred
genbm b030ce89ca openbilibili 5 rokov pred
generator b030ce89ca openbilibili 5 rokov pred
jsonpb b030ce89ca openbilibili 5 rokov pred
util b030ce89ca openbilibili 5 rokov pred
BUILD b030ce89ca openbilibili 5 rokov pred
CHANGELOG.MD b030ce89ca openbilibili 5 rokov pred
CONTRIBUTORS.MD b030ce89ca openbilibili 5 rokov pred
Makefile b030ce89ca openbilibili 5 rokov pred
OWNERS b030ce89ca openbilibili 5 rokov pred
README.md b030ce89ca openbilibili 5 rokov pred
main.go b030ce89ca openbilibili 5 rokov pred

README.md

Deprecated: 请大佬移步 https://git.bilibili.co/platform/go-common/tree/master/app/tool/bmproto 生成的代码不一样但是功能一致, 可以无缝迁移!

protoc-gen-bm

通过 protobuf 自动生成 bm server,统一数据传输模型.

安装

go install go-common/app/tool/protoc-gen-bm

如何生成 bm server 代码 (example)

# 生成 examples/helloworld bm server 代码
protoc -I. -Ithird_party/googleapis --bm_out=:. examples/helloworld/api/v1/helloworld.proto
# 使用 jsonpb
protoc -I. -Ithird_party/googleapis --bm_out=jsonpb=true:. examples/helloworld/api/v1/helloworld.proto

给 protobuf 添加 http 描述示例

service Messaging {
  rpc GetMessage(GetMessageRequest) returns (Message) {
    option (google.api.http) = {
        get: "/message";
    };
  }
}
message GetMessageRequest {
  string name = 1; // Mapped to URL path.
}
message Message {
  string text = 1; // The resource content.
}

http 到 gRPC 的映射如下

HTTP gRPC
GET /message?name=foo GetMessage(name: "foo")

对于 GET 之类不包含 Body 的请求,所有的 Request Message 将被映射到 URL Query 中。 对于嵌套的字段通过 . 分割 例如:

service Messaging {
  rpc GetMessage(GetMessageRequest) returns (Message) {
    option (google.api.http) = {
        get:"/messages"
    };
  }
}
message GetMessageRequest {
  message SubMessage {
    string subfield = 1;
  }
  string message_id = 1;
  int64 revision = 2;
  SubMessage sub = 3;
}
HTTP gRPC
GET /messages?message_id=123456&revision=2&sub.subfield=foo GetMessage(message_id: "123456" revision: 2 sub: SubMessage(subfield: "foo"))

对于包含 Body 的 http 请求,body 字段可以用来指定数据映射

service Messaging {
  rpc CreateMessage(CreateMessageRequest) returns (Message) {
    option (google.api.http) = {
      post: "/messages"
      body: "*"
    };
  }
}
message CreateMessageRequest {
  string message_id = 1; 
  string text = 2; 
}
HTTP gRPC
POST /v1/messages {"message_id": "123456", "text": "Hi!" } CreateMessage(message_id: "123456", text: "Hi!")

Response 响应

Json Response

{
    "code": 0,
    "message": "this is message",
    "data": {// Response Message Marshal as JSON}
}

Protobuf Response

message PB {
	int64 Code = 1;
	string Message = 2;
	uint64 TTL = 3;
	google.protobuf.Any Data = 4;
}

与 google.api.http 不一致的地方

  • 因为 bm 不支持 restful 格式 API,所以不支持映射参数到 Path
  • Response 多了层包装,而不是直接返回 message 主体,message 主体被放到 data 中

注意事项

bazel 编译问题

目前自动生成的 BUILD 的文件无法正常编译,需要手动修改,以 example/helloworld 项目为例,需要对 BUILD 文件进行以下修改

--- a/app/tool/protoc-gen-bm/examples/helloworld/api/v1/BUILD
+++ b/app/tool/protoc-gen-bm/examples/helloworld/api/v1/BUILD
@@ -13,8 +13,8 @@ load(
 proto_library(
     name = "v1_proto",
     srcs = ["helloworld.proto"],
-    tags = ["automanaged"],
-    deps = ["google/api/annotations.proto"],
+    tags = ["manual"],
+    deps = ["@go_googleapis//google/api:annotations_proto"],
 )

 go_proto_library(
@@ -22,14 +22,14 @@ go_proto_library(
     compilers = ["@io_bazel_rules_go//proto:go_grpc"],
     importpath = "go-common/app/tool/protoc-gen-bm/examples/helloworld/api/v1",
     proto = ":v1_proto",
-    tags = ["automanaged"],
-    deps = ["google/api/annotations.proto"],
+    tags = ["manual"],
+    deps = ["@go_googleapis//google/api:annotations_go_proto"],
 )

 go_library(
     name = "go_default_library",
     srcs = ["helloworld.pb.bm.go"],
-    embed = ["v1_go_proto"],
+    embed = [":v1_go_proto"],
     importpath = "go-common/app/tool/protoc-gen-bm/examples/helloworld/api/v1",
     tags = ["automanaged"],
     visibility = ["//visibility:public"],

恩、不解释了,应该都能看懂

需要自定义 form tag

因为 blademaster 在 Bind parameters 时默认是大小写敏感的,而且没有自动转换驼峰与下划线,所以在定义 Proto message 时需要用 gogo 自定义一些 tag

message User {
    // mid
    int64 mid = 1 [(gogoproto.moretags) = "form:\"mid\" validate:\"required,min=1\""];
}

参考 https://github.com/gogo/protobuf/blob/master/extensions.md

TODO

  • 完善错误提示
  • 更加完善的 google.api.http 支持

Roadmap

  • 修改已有的工具实现读取 google.api.http option 生成 bm server
  • 添加 bilibili.api.extra option,扩展 google.api.http

已知问题

  • 复杂结构问题,由于 http from 表达能力有限,无法很好的描述有些复杂的 protobuf message,解决: 内嵌 protobuf 或者 使用 json?
  • 老项目兼容问题,有些项目会返回类似 map[string]interface{} 之类的结构,无法再 protobuf,解决: 内嵌 json ?

参考文档

client 代码生成

iOS 代码生成

TODO

Android 代码生成

TODO

/cc @liugang @zhoujiahui