如何在 JSON 响应中过滤数据库中的字段?
How to filter fields from the database in JSON response?
我正在 golang 中制作 REST API,我想添加对过滤字段的支持,但我不知道实现它的最佳方法,假设我有这个结构代表 Album
型号
type Album struct {
ID uint64 `json:"id"`
User uint64 `json:"user"`
Name string `json:"name"`
CreatedDate time.Time `json:"createdDate"`
Privacy string `json:"privacy"`
Stars int `json:"stars"`
PicturesCount int `json:"picturesCount"`
}
和一个 returns Album
实例的函数
func GetOne(id uint64, user uint64) (Album, error) {
var album Album
sql := `SELECT * FROM "album" WHERE "id" = AND "user" = ;`
err := models.DB.QueryRow(sql, id, user).Scan(
&album.ID,
&album.User,
&album.Name,
&album.CreatedDate,
&album.Privacy,
&album.Stars,
&album.PicturesCount,
)
return album, err
}
而客户要发出这样的请求
https://api.localhost.com/albums/1/?fields=id,name,privacy
撇开明显的安全问题不谈,我的第一个想法是使用类似这样的方法过滤数据库中的字段
func GetOne(id uint64, user uint64, fields string) {
var album Album
sql := fmt.Sprintf(`SELECT %s FROM "album" WHERE "id" = AND "user" = ;`, fields)
// i don't know what to do after this
}
然后我想到将 omitempty
标记添加到所有字段并将字段设置为零值,然后再将其编码为 JSON、
- 这个有用吗?
- 哪种方法更好?
- 有没有最好的方法?
- 我将如何实施第一种方法?
谢谢。
对于您的第一个提案(仅查询请求的字段),有两种方法(回答 "would this work?" 和 "how would I go about implementing the first method?"):
- 动态创建一个(可能是匿名的)
struct
并使用 encoding/json
从那里生成 JSON。
- 实施一个包装器,将从查询返回的
*database/sql.Rows
转换为 JSON。
对于方法 (1.),您将需要以某种方式为原始 struct
中的任意属性组合创建 struct
。如reflect
cannot create a new struct type at runtime, your only chance would be to generate them at compile time。组合爆炸会使你的二进制文件膨胀,所以不要那样做。
方法(2.)要谨慎处理,只能作为最后的手段。获取请求的字段列表并使用从 DB 获得的值写出 JSON 听起来很简单并且不涉及反射。但是,您的解决方案(很可能)比 encoding/json
.
不稳定得多
在阅读您的问题时,我也考虑过使用 json:"omitempty"
struct 标记。我认为这是更好的解决方案。它既不涉及元编程,也不涉及编写您自己的 JSON 编码器,这是一件好事。请注意在某些字段丢失的情况下的影响(客户端可能必须考虑到这一点)。您可以查询 all 属性 always 并使用反射覆盖不需要的属性。
最后,以上所有解决方案都不是最优的,最好的解决方案是根本不实现该功能。我希望你有充分的理由让属性可变,我很乐意根据你的解释进一步澄清我的答案。但是,如果资源的其中一个属性太大,它可能应该是一个子资源。
我正在 golang 中制作 REST API,我想添加对过滤字段的支持,但我不知道实现它的最佳方法,假设我有这个结构代表 Album
型号
type Album struct {
ID uint64 `json:"id"`
User uint64 `json:"user"`
Name string `json:"name"`
CreatedDate time.Time `json:"createdDate"`
Privacy string `json:"privacy"`
Stars int `json:"stars"`
PicturesCount int `json:"picturesCount"`
}
和一个 returns Album
func GetOne(id uint64, user uint64) (Album, error) {
var album Album
sql := `SELECT * FROM "album" WHERE "id" = AND "user" = ;`
err := models.DB.QueryRow(sql, id, user).Scan(
&album.ID,
&album.User,
&album.Name,
&album.CreatedDate,
&album.Privacy,
&album.Stars,
&album.PicturesCount,
)
return album, err
}
而客户要发出这样的请求
https://api.localhost.com/albums/1/?fields=id,name,privacy
撇开明显的安全问题不谈,我的第一个想法是使用类似这样的方法过滤数据库中的字段
func GetOne(id uint64, user uint64, fields string) {
var album Album
sql := fmt.Sprintf(`SELECT %s FROM "album" WHERE "id" = AND "user" = ;`, fields)
// i don't know what to do after this
}
然后我想到将 omitempty
标记添加到所有字段并将字段设置为零值,然后再将其编码为 JSON、
- 这个有用吗?
- 哪种方法更好?
- 有没有最好的方法?
- 我将如何实施第一种方法?
谢谢。
对于您的第一个提案(仅查询请求的字段),有两种方法(回答 "would this work?" 和 "how would I go about implementing the first method?"):
- 动态创建一个(可能是匿名的)
struct
并使用encoding/json
从那里生成 JSON。 - 实施一个包装器,将从查询返回的
*database/sql.Rows
转换为 JSON。
对于方法 (1.),您将需要以某种方式为原始 struct
中的任意属性组合创建 struct
。如reflect
cannot create a new struct type at runtime, your only chance would be to generate them at compile time。组合爆炸会使你的二进制文件膨胀,所以不要那样做。
方法(2.)要谨慎处理,只能作为最后的手段。获取请求的字段列表并使用从 DB 获得的值写出 JSON 听起来很简单并且不涉及反射。但是,您的解决方案(很可能)比 encoding/json
.
在阅读您的问题时,我也考虑过使用 json:"omitempty"
struct 标记。我认为这是更好的解决方案。它既不涉及元编程,也不涉及编写您自己的 JSON 编码器,这是一件好事。请注意在某些字段丢失的情况下的影响(客户端可能必须考虑到这一点)。您可以查询 all 属性 always 并使用反射覆盖不需要的属性。
最后,以上所有解决方案都不是最优的,最好的解决方案是根本不实现该功能。我希望你有充分的理由让属性可变,我很乐意根据你的解释进一步澄清我的答案。但是,如果资源的其中一个属性太大,它可能应该是一个子资源。