2

我有一些 REST API,我的模型定义为 Go 结构。

type User struct {
  FirstName string
  LastName  string
}

然后我有了获取数据的数据库方法。

GetUserByID(id int) (*User, error)

现在我想用https://github.com/twitchtv/twirp替换我的 REST API 。

因此我开始在.proto文件中定义我的模型。

message User {
  string first_name = 2;
  string last_name = 3;
}

现在我有两种User类型。让我们称它们为原生类型和原型类型。

我还在我的.proto文件中定义了一个服务,它将用户返回到前端。

service Users {
  rpc GetUser(Id) returns (User);
}

这会生成一个我必须填写的界面。

func (s *Server) GetUser(context.Context, id) (*User, error) {
  // i'd like to reuse my existing database methods
  u, err := db.GetUserByID(id)
  // handle error
  // do more stuff
  return u, nil
}

不幸的是,这不起作用。我的数据库返回一个本地用户,但界面需要一个原始用户。

有没有简单的方法让它工作?也许使用type aliases

非常感谢!

4

2 回答 2

1

解决问题的一种方法是手动进行转换。

type User struct {
    FirstName string
    LastName string
}

type protoUser struct {
    firstName string
    lastName string
}

func main() {
    u := db() // Retrieve a user from a mocked db

    fmt.Println("Before:")
    fmt.Printf("%#v\n", *u) // What db returns (*protoUser)
    fmt.Println("After:")
    fmt.Printf("%#v\n", u.AsUser()) // What conversion returns (User)
}

// Mocked db that returns pointer to protoUser
func db() *protoUser {
    pu := protoUser{"John", "Dough"}
    return &pu
}

// Conversion method (converts protoUser into a User)
func (pu *protoUser) AsUser() User {
    return User{pu.firstName, pu.lastName}
}

关键部分是结构AsUser上的方法protoUser
在那里,我们只需编写自定义逻辑,将 aprotoUser转换为User我们想要使用的类型。

工作示例

于 2018-04-10T12:35:51.683 回答
0

正如@Peter在评论部分提到的。

我见过一个使用自定义Convert功能制作的项目。它通过 将 Protobuf 转换为本地结构json.Unmarshal,不确定性能如何,但这是一种方法。

预览代码 PLAYGROUND

// Convert converts the in struct to out struct via `json.Unmarshal`
func Convert(in interface{}, out interface{}) error {
    j, err := json.Marshal(in)
    if err != nil {
        return err
    }
    err = json.Unmarshal(j, &out)
    if err != nil {
        return err
    }
    return nil
}

func main() {
    // Converts the protobuf struct to local struct via json.Unmarshal
    var localUser User
    if err := convert(protoUser, &localUser); err != nil {
        panic(err)
    }
}

输出

Before:
main.ProtoUser{FirstName:"John", LastName:"Dough"}
After:
main.User{FirstName:"John", LastName:"Dough"}

Program exited.
于 2019-11-16T07:20:31.163 回答