英文:
Golang + MongoDB embedded type (embedding a struct in another struct)
问题
假设我运行一个API,当用户对用户资源发出GET请求时,我将返回相关字段作为JSON。
type User struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
Secret string `json:"-,omitempty" bson:"secret,omitempty"`
}
如你所见,User中的Secret字段有json:"-"
。这意味着在大多数操作中,我不希望返回该字段。在这种情况下,响应将是:
{
"id": 1,
"Name": "John"
}
由于json:"-"
省略了该字段,因此不会返回Secret字段。
现在,我正在开放一个仅限管理员的路由,我希望返回Secret字段。然而,这意味着需要复制User结构体。
我的当前解决方案如下:
type adminUser struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
Secret string `json:"secret,omitempty" bson:"secret,omitempty"`
}
是否有一种将User嵌入到adminUser中的方法?类似于继承:
type adminUser struct {
User
Secret string `json:"secret,omitempty" bson:"secret,omitempty"`
}
上述方法目前不起作用,因为在这种情况下只会返回Secret字段。
注意:在实际的代码库中,有几十个字段。因此,复制代码的成本很高。
实际的Mongo查询如下:
func getUser(w http.ResponseWriter, r *http.Request) {
....省略的代码....
var user adminUser
err := common.GetDB(r).C("users").Find(
bson.M{"_id": userId},
).One(&user)
if err != nil {
return
}
common.ServeJSON(w, &user)
}
英文:
Hypothetical, I run an API and when a user makes a GET request on the user resource, I will return relevant fields as a JSON
type User struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
Secret string `json:"-,omitempty" bson:"secret,omitempty"`
}
As you can see, the Secret field in User has json:"-"
. This implies that in most operation that I would not like to return. In this case, a response would be
{
"id":1,
"Name": "John"
}
The field secret will not be returned as json:"-"
omits the field.
Now, I am openning an admin only route where I would like to return the secret field. However, that would mean duplicating the User struct.
My current solution looks like this:
type adminUser struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
Name string `json:"name,omitempty" bson:"name,omitempty"`
Secret string `json:"secret,omitempty" bson:"secret,omitempty"`
}
Is there a way to embed User into adminUser? Kind of like inheritance:
type adminUser struct {
User
Secret string `json:"secret,omitempty" bson:"secret,omitempty"`
}
The above currently does not work, as only the field secret will be returned in this case.
Note: In the actual code base, there are few dozens fields. As such, the cost of duplicating code is high.
The actual mongo query is below:
func getUser(w http.ResponseWriter, r *http.Request) {
....omitted code...
var user adminUser
err := common.GetDB(r).C("users").Find(
bson.M{"_id": userId},
).One(&user)
if err != nil {
return
}
common.ServeJSON(w, &user)
}
答案1
得分: 69
你应该查看bson包的inline标志(在bson.Marshal下有文档记录)。它应该允许你做类似这样的事情:
type adminUser struct {
User `bson:",inline"`
Secret string `json:"secret,omitempty" bson:"secret,omitempty"`
}
然而,现在你会注意到,当你尝试使用这个结构从数据库中读取时,会出现重复键错误,因为adminUser
和User
都包含secret
键。
在你的情况下,我会从User
中删除Secret
字段,只在adminUser
中保留一个。然后,每当你需要写入secret
字段时,确保你使用的是adminUser
。
英文:
You should take a look at the bson package's inline flag
(that is documented under bson.Marshal).
It should allow you to do something like this:
type adminUser struct {
User `bson:",inline"`
Secret string `json:"secret,omitempty" bson:"secret,omitempty"`
}
However, now you'll notice that you get duplicate key errors
when you try to read from the database with this structure,
since both adminUser
and User
contain the key secret
.
In your case I would remove the Secret
field from User
and only have the one in adminUser
.
Then whenever you need to write to the secret
field,
make sure you use an adminUser
.
答案2
得分: 1
另一种选择是声明一个接口。
type SecureModel interface {
SecureMe()
}
确保你的模型实现了它:
type User struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
Username string `json:"username" bson:"username"`
Secret string `json:"secret,omitempty" bson:"secret"`
}
func (u *User) SecureMe() {
u.Secret = ""
}
然后根据调用的路由来调用它。
// 我被发送给非管理员,保护我。
if _, ok := user.(SecureModel); ok {
user.(SecureModel).SecureMe()
}
// 转换为JSON,等等。
...
编辑:在这里使用接口的原因是为了在使用通用方法发送任意模型的情况下。
英文:
Another alternative would be to declare an interface.
type SecureModel interface {
SecureMe()
}
Make sure your model implements it:
type User struct {
Id bson.ObjectId `json:"id,omitempty" bson:"_id,omitempty"`
Username string `json:"username" bson:"username"`
Secret string `json:"secret,omitempty" bson:"secret"`
}
func (u *User) SecureMe() {
u.Secret = ""
}
And only call it depending on which route is called.
// I am being sent to a non-admin, secure me.
if _, ok := user.(SecureModel); ok {
user.(SecureModel).SecureMe()
}
// Marshall to JSON, etc.
...
Edit: The reason for using an interface here is for cases where you might send arbitrary models over the wire using a common method.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论