I'm writing a webservice in Go using Echo framework and Gorm. I have a User
struct that looks like this:
我正在编写一个使用Echo framework和Gorm的webservice。我有一个这样的用户结构:
type User struct {
ID uint `json:"user_id"`
Email string `json:"email_address,omitempty" validate:"required,email"`
}
I'm accepting POST
requests with a Content-type: application/json
. I want my input to be {"email_address": "email@email.com"}
and my output to be {"user_id": 1}
我接受内容类型为application/json的POST请求。我希望我的输入是{"email_address": "email@email.com"},输出是{"user_id": 1}
How can I forbid users from submitting ID
in the request (so they can't create records with certain ID
s), but keep ID
in the response? Right now I'm doing user.ID = 0
right before save, but I wonder if there's a better way to do it?
我怎么能禁止用户在请求中提交ID(这样他们就不能用某些ID创建记录),而在响应中保留ID呢?现在我在做用户。在保存之前ID = 0,但我想知道有没有更好的方法?
I also want to skip Email
in output. Right now I'm doing user.Email = ""
right before the output. Is there a better way for that also?
我还想在输出中跳过电子邮件。现在我在做用户。电子邮件= "" "在输出前。还有更好的方法吗?
Thanks!
谢谢!
2 个解决方案
#1
1
I extend your example by making it more general, and I show an elegant solution to the more general problem.
我扩展了您的示例,使其更一般化,并展示了更一般问题的优雅解决方案。
Let's assume that in User
there are:
假设用户中有:
- some fields that must be parsed only from the input
- 必须仅从输入解析的一些字段
- some fields that must appear only in the output
- 某些字段必须只出现在输出中
- and there are some fields that must be parsed from input and must also appear in output
- 还有一些字段必须从输入中解析,也必须出现在输出中
Your example is a "subset" of this (as in your example there are no common fields).
您的示例是这个的“子集”(在您的示例中没有公共字段)。
This can be elegantly solved without repeating fields using embedding. You may create 3 separate types; one for the common fields UserBoth
, one for the input-only fields UserIn
, and one for the output-only fields UserOut
:
这可以在不重复使用嵌入字段的情况下优雅地解决。您可以创建三个单独的类型;一个用于公共字段UserBoth,一个用于仅输入字段UserIn,一个用于仅输出字段UserOut:
type UserBoth struct {
Name string `json:"name"`
Age int `json:"age"`
}
type UserIn struct {
*UserBoth
Email string `json:"email"`
}
type UserOut struct {
*UserBoth
ID uint `json:"id"`
}
Note that UserBoth
(or rather *UserBoth
to avoid duplicating the struct value) is embedded in both UserIn
and UserOut
, because we want those fields in both.
注意UserBoth(或者更确切地说是*UserBoth,以避免重复struct值)都嵌入到UserIn和UserOut中,因为我们希望这两个字段都包含。
Now if you have an input that contains all fields (even though we don't want all):
现在,如果你有一个包含所有字段的输入(即使我们不需要所有字段):
const in = `{"name":"Bob","age":23,"Email":"as@as.com","id":123}`
Unmarshaling into a value of UserIn
will only parse what you want:
解组到UserIn值将只解析您想要的:
uin := UserIn{UserBoth: &UserBoth{}}
err := json.Unmarshal([]byte(in), &uin)
fmt.Printf("%+v %+v %v\n", uin, uin.UserBoth, err)
Output (note that the Email
field is parsed but ID
isn't):
输出(注意邮件字段被解析,但ID不是):
{UserBoth:0x1050e150 Email:as@as.com} &{Name:Bob Age:23} <nil>
And when you want to generate output:
当你想要产生输出:
uout := UserOut{UserBoth: uin.UserBoth}
// Fetch / set ID from db:
uout.ID = 456
out, err := json.Marshal(uout)
fmt.Println(string(out), err)
Output (note that the ID
field is present but Email
isn't):
输出(注意ID字段存在,但电子邮件不存在):
{"name":"Bob","age":23,"id":456} <nil>
Try it on the Go Playground.
在操场上试试。
Having a "unified" User
The above example used 2 different structs: UserIn
for parsing and UserOut
to generate the output.
上面的例子使用了两个不同的结构:UserIn用于解析和UserOut生成输出。
If inside your code you want to use a "unified" User
struct, this is how it can be done:
如果您想在代码中使用“统一”用户结构,可以这样做:
type User struct {
UserIn
UserOut
}
Using it:
使用它:
uboth := &UserBoth{}
u := User{UserIn{UserBoth: uboth}, UserOut{UserBoth: uboth}}
err := json.Unmarshal([]byte(in), &u.UserIn)
fmt.Printf("%+v %+v %v\n", u, u.UserIn.UserBoth, err)
// Fetch / set ID from db:
u.ID = 456
out, err := json.Marshal(u.UserOut)
fmt.Println(string(out), err)
Try this one on the Go Playground.
试试这个在Go游乐场。
#2
0
While icza's answer proposes a nice solution, you could also employ JSON marshaling auxiliary methods MarshalJSON/UnmarshalJSON
:
虽然icza的回答提出了一个不错的解决方案,但您也可以使用JSON编组辅助方法MarshalJSON/UnmarshalJSON:
func main() {
// employing auxiliary json methods MarshalJSON/UnmarshalJSON
user := User{ID: 123, Email: `abc@xyz.com`}
js, _ := json.Marshal(&user)
log.Printf("%s", js)
input := UserInput(user)
js, _ = json.Marshal(&input)
log.Printf("%s", js)
output := UserOutput(user)
js, _ = json.Marshal(&output)
log.Printf("%s", js)
}
type User struct {
ID uint `json:"user_id"`
Email string `json:"email_address,omitempty" validate:"required,email"`
}
type UserInput User
func (x *UserInput) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Email string `json:"email_address,omitempty" validate:"required,email"`
}{
Email: x.Email,
})
}
type UserOutput User
func (x *UserOutput) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
ID uint `json:"user_id"`
}{
ID: x.ID,
})
}
Which gives us:
这给我们:
[ info ] main.go:25: {"user_id":123,"email_address":"abc@xyz.com"}
[ info ] main.go:29: {"email_address":"abc@xyz.com"}
[ info ] main.go:33: {"user_id":123}
On Go Playground.
去操场上。
#1
1
I extend your example by making it more general, and I show an elegant solution to the more general problem.
我扩展了您的示例,使其更一般化,并展示了更一般问题的优雅解决方案。
Let's assume that in User
there are:
假设用户中有:
- some fields that must be parsed only from the input
- 必须仅从输入解析的一些字段
- some fields that must appear only in the output
- 某些字段必须只出现在输出中
- and there are some fields that must be parsed from input and must also appear in output
- 还有一些字段必须从输入中解析,也必须出现在输出中
Your example is a "subset" of this (as in your example there are no common fields).
您的示例是这个的“子集”(在您的示例中没有公共字段)。
This can be elegantly solved without repeating fields using embedding. You may create 3 separate types; one for the common fields UserBoth
, one for the input-only fields UserIn
, and one for the output-only fields UserOut
:
这可以在不重复使用嵌入字段的情况下优雅地解决。您可以创建三个单独的类型;一个用于公共字段UserBoth,一个用于仅输入字段UserIn,一个用于仅输出字段UserOut:
type UserBoth struct {
Name string `json:"name"`
Age int `json:"age"`
}
type UserIn struct {
*UserBoth
Email string `json:"email"`
}
type UserOut struct {
*UserBoth
ID uint `json:"id"`
}
Note that UserBoth
(or rather *UserBoth
to avoid duplicating the struct value) is embedded in both UserIn
and UserOut
, because we want those fields in both.
注意UserBoth(或者更确切地说是*UserBoth,以避免重复struct值)都嵌入到UserIn和UserOut中,因为我们希望这两个字段都包含。
Now if you have an input that contains all fields (even though we don't want all):
现在,如果你有一个包含所有字段的输入(即使我们不需要所有字段):
const in = `{"name":"Bob","age":23,"Email":"as@as.com","id":123}`
Unmarshaling into a value of UserIn
will only parse what you want:
解组到UserIn值将只解析您想要的:
uin := UserIn{UserBoth: &UserBoth{}}
err := json.Unmarshal([]byte(in), &uin)
fmt.Printf("%+v %+v %v\n", uin, uin.UserBoth, err)
Output (note that the Email
field is parsed but ID
isn't):
输出(注意邮件字段被解析,但ID不是):
{UserBoth:0x1050e150 Email:as@as.com} &{Name:Bob Age:23} <nil>
And when you want to generate output:
当你想要产生输出:
uout := UserOut{UserBoth: uin.UserBoth}
// Fetch / set ID from db:
uout.ID = 456
out, err := json.Marshal(uout)
fmt.Println(string(out), err)
Output (note that the ID
field is present but Email
isn't):
输出(注意ID字段存在,但电子邮件不存在):
{"name":"Bob","age":23,"id":456} <nil>
Try it on the Go Playground.
在操场上试试。
Having a "unified" User
The above example used 2 different structs: UserIn
for parsing and UserOut
to generate the output.
上面的例子使用了两个不同的结构:UserIn用于解析和UserOut生成输出。
If inside your code you want to use a "unified" User
struct, this is how it can be done:
如果您想在代码中使用“统一”用户结构,可以这样做:
type User struct {
UserIn
UserOut
}
Using it:
使用它:
uboth := &UserBoth{}
u := User{UserIn{UserBoth: uboth}, UserOut{UserBoth: uboth}}
err := json.Unmarshal([]byte(in), &u.UserIn)
fmt.Printf("%+v %+v %v\n", u, u.UserIn.UserBoth, err)
// Fetch / set ID from db:
u.ID = 456
out, err := json.Marshal(u.UserOut)
fmt.Println(string(out), err)
Try this one on the Go Playground.
试试这个在Go游乐场。
#2
0
While icza's answer proposes a nice solution, you could also employ JSON marshaling auxiliary methods MarshalJSON/UnmarshalJSON
:
虽然icza的回答提出了一个不错的解决方案,但您也可以使用JSON编组辅助方法MarshalJSON/UnmarshalJSON:
func main() {
// employing auxiliary json methods MarshalJSON/UnmarshalJSON
user := User{ID: 123, Email: `abc@xyz.com`}
js, _ := json.Marshal(&user)
log.Printf("%s", js)
input := UserInput(user)
js, _ = json.Marshal(&input)
log.Printf("%s", js)
output := UserOutput(user)
js, _ = json.Marshal(&output)
log.Printf("%s", js)
}
type User struct {
ID uint `json:"user_id"`
Email string `json:"email_address,omitempty" validate:"required,email"`
}
type UserInput User
func (x *UserInput) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
Email string `json:"email_address,omitempty" validate:"required,email"`
}{
Email: x.Email,
})
}
type UserOutput User
func (x *UserOutput) MarshalJSON() ([]byte, error) {
return json.Marshal(&struct {
ID uint `json:"user_id"`
}{
ID: x.ID,
})
}
Which gives us:
这给我们:
[ info ] main.go:25: {"user_id":123,"email_address":"abc@xyz.com"}
[ info ] main.go:29: {"email_address":"abc@xyz.com"}
[ info ] main.go:33: {"user_id":123}
On Go Playground.
去操场上。