hz 使用 (protobuf)

基于 protobuf IDL 创建项目

new: 创建一个新项目

  1. 在当前目录下创建 protobuf idl 文件
  1. 为在 protobuf 中支持 api 注解,请在使用了注解的 proto 文件中,import 下面的文件

  2. 如果想自行拓展注解的使用,请不要以"5"作为序号的开头,避免出现冲突。例如 “optional string xxx = 77777;”

// idl/api.proto; 注解拓展
syntax = "proto2";

package api;

import "google/protobuf/descriptor.proto";

option go_package = "/api";

extend google.protobuf.FieldOptions {
  optional string raw_body = 50101;
  optional string query = 50102;
  optional string header = 50103;
  optional string cookie = 50104;
  optional string body = 50105;
  optional string path = 50106;
  optional string vd = 50107;
  optional string form = 50108;
  optional string go_tag = 51001;
  optional string js_conv = 50109;
}

extend google.protobuf.MethodOptions {
  optional string get = 50201;
  optional string post = 50202;
  optional string put = 50203;
  optional string delete = 50204;
  optional string patch = 50205;
  optional string options = 50206;
  optional string head = 50207;
  optional string any = 50208;
  optional string gen_path = 50301;
  optional string api_version = 50302;
  optional string tag = 50303;
  optional string name = 50304;
  optional string api_level = 50305;
  optional string serializer = 50306;
  optional string param = 50307;
  optional string baseurl = 50308;
}

extend google.protobuf.EnumValueOptions {
  optional int32 http_code = 50401;
}

主 idl 定义:

// idl/hello/hello.proto
syntax = "proto3";

package hello;

option go_package = "hertz/hello";

import "api.proto";

message HelloReq {
  string Name = 1[(api.query)="name"];
}

message HelloResp {
  string RespBody = 1;
}

service HelloService {
  rpc Method1(HelloReq) returns(HelloResp) {
    option (api.get) = "/hello";
  }
}
  1. 创建新项目
// GOPATH 下执行,如果主 IDL 的依赖和主 IDL 不在同一路径下,需要加入 "-I" 选项,其含义为 IDL 搜索路径,等同于 protoc 的 "-I" 命令
hz new -I idl -idl idl/hello/hello.proto

go mod init

// 整理 & 拉取依赖
go mod tidy
// 非 GOPATH 下执行,需要指定 go mod 名,如果主 IDL 的依赖和主 IDL 不在同一路径下,需要加入 "-I" 选项,其含义为 IDL 搜索路径,等同于 protoc 的 "-I" 命令

hz new -module example.com/m -I idl -idl idl/hello/hello.proto

// 整理 & 拉取依赖
go mod tidy

  1. 修改 handler,添加自己的逻辑
// handler path: biz/handler/hello/hello_service.go
// 其中 "/hello" 是 protobuf idl 中 go_package 的最后一级
// "hello_service.go" 是 protobuf idl 中 service 的名字,所有 service 定义的方法都会生成在这个文件中

// Method1 .
// @router /hello [GET]
func Method1(ctx context.Context, c *app.RequestContext) {
   var err error
   var req hello.HelloReq
   err = c.BindAndValidate(&req)
   if err != nil {
      c.String(400, err.Error())
      return
   }

   resp := new(hello.HelloResp)

   // 你可以修改整个函数的逻辑,而不仅仅局限于当前模板
   resp.RespBody = "hello," + req.Name // 添加的逻辑

   c.JSON(200, resp)
}
  1. 编译项目
go build
  1. 运行项目并测试

运行项目:

./{{your binary}}

测试:

curl --location --request GET 'http://127.0.0.1:8888/hello?name=hertz'

如果返回{"RespBody":"hello,hertz"},说明接口调通。

update: 更新一个已有的项目

  1. 如果你的 protobuf idl 有更新,例如:
// idl/hello/hello.proto
syntax = "proto3";

package hello;

option go_package = "hertz/hello";

import "api.proto";

message HelloReq {
  string Name = 1[(api.query)="name"];
}

message HelloResp {
  string RespBody = 1;
}

message OtherReq {
  string Other = 1[(api.body)="other"];
}

message OtherResp {
  string Resp = 1;
}

service HelloService {
  rpc Method1(HelloReq) returns(HelloResp) {
    option (api.get) = "/hello";
  }
  rpc Method2(OtherReq) returns(OtherResp) {
    option (api.post) = "/other";
  }
}

service NewService {
  rpc Method3(OtherReq) returns(OtherResp) {
    option (api.get) = "/new";
  }
}
  1. 切换到执行 new 命令的目录,更新修改后的 protobuf idl
hz update -I idl -idl idl/hello/hello.proto
  1. 可以看到 在"biz/handler/hello/hello_service.go" 下新增了新的方法 在"biz/handler/hello" 下新增了文件 “new_service.go” 以及对应的 “Method3” 方法。

下面我们来开发 “Method2” 接口

// Method1 .
// @router /hello [GET]
func Method1(ctx context.Context, c *app.RequestContext) {
   var err error
   var req hello.HelloReq
   err = c.BindAndValidate(&req)
   if err != nil {
      c.String(400, err.Error())
      return
   }

   resp := new(hello.HelloResp)

   // 你可以修改整个函数的逻辑,而不仅仅局限于当前模板
   resp.RespBody = "hello," + req.Name // 添加的逻辑

   c.JSON(200, resp)
}

// Method2 .
// @router /other [POST]
func Method2(ctx context.Context, c *app.RequestContext) {
   var err error
   var req hello.OtherReq
   err = c.BindAndValidate(&req)
   if err != nil {
      c.String(400, err.Error())
      return
   }

   resp := new(hello.OtherResp)

   // 增加的逻辑
   resp.Resp = "Other method: " + req.Other

   c.JSON(200, resp)
}
  1. 编译项目
go build
  1. 运行项目并测试

运行项目:

./{{your binary}}

测试:

curl --location --request POST 'http://127.0.0.1:8888/other' \
--header 'Content-Type: application/json' \
--data-raw '{
    "Other": "other method"
}'

如果返回{"Resp":"Other method: other method"},说明接口调通。


最后修改 July 5, 2023 : Update response.md (11394d6)