php小編百草在介紹gRPC的使用時指出,如果在gRPC請求中,指定的值為false,那么gRPC將不會返回布爾值。這意味著在使用gRPC時,我們需要注意如何處理返回值,以免造成混淆和錯誤。了解這個細節將幫助我們更好地理解和應用gRPC的功能,提高我們的編程效率和代碼質量。讓我們一起深入研究gRPC的更多特性和用法,為我們的項目帶來更好的性能和擴展性。
問題內容
func (m *todoserver) gettodos(ctx context.context, empty *emptypb.empty) (*desc.gettodosresponse, error) { todos, err := m.todoservice.gettodos() if err != nil { return nil, err } todosresp := make([]*desc.gettodosresponse_todo, 0, len(todos)) for _, todo := range todos { todosresp = append(todosresp, &desc.gettodosresponse_todo{ id: todo.id, title: todo.title, iscompleted: todo.iscompleted, }) } return &desc.gettodosresponse{todos: todosresp}, nil }
登錄后復制
service TodoService { rpc GetTodos(google.protobuf.Empty) returns (GetTodosResponse) {} } message GetTodosResponse { repeated Todo todos = 1; message Todo { int64 id = 1; string title = 2; bool is_completed = 3; } }
登錄后復制登錄后復制
service TodoService { rpc GetTodos(google.protobuf.Empty) returns (GetTodosResponse) {} } message GetTodosResponse { repeated Todo todos = 1; message Todo { int64 id = 1; string title = 2; bool is_completed = 3; } }
登錄后復制登錄后復制
我在數據庫中有一條記錄
|編號 |標題 |已完成 |
|-|-|-|
| 1 |啊啊|假|
上面的函數返回 {"todos": [{"id": "1", "title": "aaa"}]}
但一旦我將 is_completed
更改為 true
,結果是正確的 {"todos ": [{"id": "1", "title": "aaa", "iscompleted": true}]}
解決方法
這是設計使然,也是為了提高效率。
bool
的“零”值是 false
– 因此,當使用 false
值初始化 protobuf
結構時,在使用標準庫的 encoding/json
解組器時不需要顯式聲明該字段。在編碼端,如果字段的 json 標記包含 omitempty
限定符,則標準庫的 encoding/json
封送拆收器將刪除任何零值 – 這就是您所看到的。
如果 title
字符串字段是 ""
(即字符串的零值),您將看到相同的行為。
查看生成的代碼(*.pb.go
),結構體的 bool
字段定義將如下所示:
type todo struct { // ... iscompleted bool `protobuf:"varint,5,opt,name=is_complete,proto3" json:"is_complete,omitempty"` }
登錄后復制
因此 json:"...,omitempty"
指示 encoding/json
封送拆收器在使用這些標簽進行封送期間省略任何零值。
如果您想覆蓋此行為:
可以從生成的代碼中刪除 omitempty
指令(不推薦 – 因為需要在開發的生命周期中管理編輯)。但如果您必須這樣做,請參閱此答案;
如果使用 grpc-gateway
,請在運行時覆蓋它,例如
gwmux := runtime.newservemux(runtime.withmarshaleroption(runtime.mimewildcard, &runtime.jsonpb{origname: true, emitdefaults: true}))
登錄后復制
或者,如果自己導出 json,則不使用標準庫 (encoding/json
),而是使用此包中的 json
封送拆收器 "google.golang.org/protobuf/encoding/protojson"
:
protojson.Marshaler{EmitDefaults: true}.Marshal(w, resp)
登錄后復制
如此答案中所述。