本文最后更新于:2023年7月26日 晚上
前文
由于项目组用了一个轻量的golang rpc框架——drpc,出现了http接口,json字段omitempty无法配置,导致restful接口,字段值是对应类型的零值时,接口不是返回零值,而是直接不返回该字段。如接口预期返回是:
{
"code": 0,
"message": "hello world",
"data": []
}
实际drpc的restful服务返回的是:
{
"message": "hello world"
}
因此研究了下drpc的源码,并进行了相应改造,解决这一问题
定位
通过观察pb文件,发现drpc使用的json marshal工具不是内置的encording/json 包,而是 protojson 包
再看protojson 的MarshalOptions源码,就很清晰了,EmitUnpopulated 就是类似json tag的omitempty
// MarshalOptions is a configurable JSON format marshaler.
type MarshalOptions struct {
pragma.NoUnkeyedLiterals
// Multiline specifies whether the marshaler should format the output in
// indented-form with every textual element on a new line.
// If Indent is an empty string, then an arbitrary indent is chosen.
Multiline bool
// Indent specifies the set of indentation characters to use in a multiline
// formatted output such that every entry is preceded by Indent and
// terminated by a newline. If non-empty, then Multiline is treated as true.
// Indent can only be composed of space or tab characters.
Indent string
// AllowPartial allows messages that have missing required fields to marshal
// without returning an error. If AllowPartial is false (the default),
// Marshal will return error if there are any missing required fields.
AllowPartial bool
// UseProtoNames uses proto field name instead of lowerCamelCase name in JSON
// field names.
UseProtoNames bool
// UseEnumNumbers emits enum values as numbers.
UseEnumNumbers bool
// EmitUnpopulated specifies whether to emit unpopulated fields. It does not
// emit unpopulated oneof fields or unpopulated extension fields.
// The JSON value emitted for unpopulated fields are as follows:
// ╔═══════╤════════════════════════════╗
// ║ JSON │ Protobuf field ║
// ╠═══════╪════════════════════════════╣
// ║ false │ proto3 boolean fields ║
// ║ 0 │ proto3 numeric fields ║
// ║ "" │ proto3 string/bytes fields ║
// ║ null │ proto2 scalar fields ║
// ║ null │ message fields ║
// ║ [] │ list fields ║
// ║ {} │ map fields ║
// ╚═══════╧════════════════════════════╝
EmitUnpopulated bool
// Resolver is used for looking up types when expanding google.protobuf.Any
// messages. If nil, this defaults to using protoregistry.GlobalTypes.
Resolver interface {
protoregistry.ExtensionTypeResolver
protoregistry.MessageTypeResolver
}
}
接下来的工作就是让drpc生成的pb代码中,protojson.Marshal 方法使用这个配置。所以再回到drpc源码,找到生成这一段代码的位置:
并做如下修改:
剩下的工作就是把protoc-gen-go-drpc工具重新编译即可,最终生成的pb文件,protocjson.Marshal方法也成功使用了MarshalOptions配置:
附上生成drpc pb代码的镜像
FROM golang:1.19-alpine3.16 as builder
ENV GOPROXY=https://goproxy.cn,direct
WORKDIR /src
COPY . /src
RUN go mod download -x
RUN go build -o /bin/protoc-gen-go cmd/protoc-gen-go/main.go
RUN go build -o /bin/protoc-gen-go-drpc main.go
FROM alpine:3.16
RUN apk update
RUN apk add protobuf
COPY --from=builder /bin/protoc-gen-go-drpc /bin
COPY --from=builder /bin/protoc-gen-go /bin
WORKDIR /protobuf
ENTRYPOINT protoc --go_out=/out --go-drpc_out=/out --proto_path=. $(find ./ -name '*.proto')
# docker build -f Dockerfile -t tc/protoc-drpc .
# docker run --rm -v $(pwd)/protobuf:/protobuf -v $(pwd):/out tc/protoc-drpc
PS
debug期间也对protobuf-go工具,做了修改,实现生成的pb文件的字段,json tag 不携带 omitempty,具体修改如下:
最后,非必要还是用grpc这种社区更活跃的框架吧
本博客所有文章除特别声明外,均采用 CC BY-SA 3.0协议 。转载请注明出处!