Требования
Два сервиса:
- Сервер - для записи сообщений в блоге в MongoDB
- Клиент - отправляет запрос первому сервису
В блоге есть title
типа string
, а content
, который является типом динамического c, может иметь любое значение JSON.
Protobuf
syntax = "proto3";
package blog;
option go_package = "blogpb";
import "google/protobuf/struct.proto";
message Blog {
string id = 1;
string title = 2;
google.protobuf.Value content = 3;
}
message CreateBlogRequest {
Blog blog = 1;
}
message CreateBlogResponse {
Blog blog = 1;
}
service BlogService {
rpc CreateBlog (CreateBlogRequest) returns (CreateBlogResponse);
}
Начнем с сообщения protobuf, которое удовлетворяет требованиям - string
для title
и любому значению JSON для content
.
Client
package main
import (...)
func main() {
cc, _ := grpc.Dial("localhost:50051", grpc.WithInsecure())
defer cc.Close()
c := blogpb.NewBlogServiceClient(cc)
var blog blogpb.Blog
json.Unmarshal([]byte(`{"title": "First example", "content": "string"}`), &blog)
c.CreateBlog(context.Background(), &blogpb.CreateBlogRequest{Blog: &blog})
json.Unmarshal([]byte(`{"title": "Second example", "content": {"foo": "bar"}}`), &blog)
c.CreateBlog(context.Background(), &blogpb.CreateBlogRequest{Blog: &blog})
}
Клиент отправляет на сервер два запроса - один с content
типа string
, а другой с object
. Здесь нет ошибок.
Сервер
package main
import (...)
var collection *mongo.Collection
type server struct {
}
type blogItem struct {
ID primitive.ObjectID `bson:"_id,omitempty"`
Title string `bson:"title"`
Content *_struct.Value `bson:"content"`
}
func (*server) CreateBlog(ctx context.Context, req *blogpb.CreateBlogRequest) (*blogpb.CreateBlogResponse, error) {
blog := req.GetBlog()
data := blogItem{
Title: blog.GetTitle(),
Content: blog.GetContent(),
}
// TODO: convert "data" or "data.Content" to something that could be BSON encoded..
res, err := collection.InsertOne(context.Background(), data)
if err != nil {
log.Fatal(err)
}
oid, _ := res.InsertedID.(primitive.ObjectID)
return &blogpb.CreateBlogResponse{
Blog: &blogpb.Blog{
Id: oid.Hex(),
Title: blog.GetTitle(),
Content: blog.GetContent(),
},
}, nil
}
func main() {
client, _ := mongo.NewClient(options.Client().ApplyURI("mongodb://localhost:27017"))
client.Connect(context.TODO())
collection = client.Database("mydb").Collection("blog")
lis, _ := net.Listen("tcp", "0.0.0.0:50051")
s := grpc.NewServer([]grpc.ServerOption{}...)
blogpb.RegisterBlogServiceServer(s, &server{})
reflection.Register(s)
go func() { s.Serve(lis) }()
ch := make(chan os.Signal, 1)
signal.Notify(ch, os.Interrupt)
<-ch
client.Disconnect(context.TODO())
lis.Close()
s.Stop()
}
И здесь я получаю:
не может преобразовать тип main.blogItem в документ BSON: не найден кодировщик для structpb .isValue_Kind
Чего мне ожидать? Чтобы увидеть точное значение содержимого в MongoDB, что-то вроде этого:
{ "_id" : ObjectId("5e5f6f75d2679d058eb9ef79"), "title" : "Second example", "content": "string" }
{ "_id" : ObjectId("5e5f6f75d2679d058eb9ef78"), "title" : "First example", "content": { "foo": "bar" } }
Я думаю, мне нужно преобразовать data.Content
в строку, где я добавил TODO:
...
Я могу создать репозиторий github на этом примере, если это поможет.