如何在JSON响应中过滤数据库中的字段?

时间:2016-05-06 15:09:27

标签: rest go json-api

我在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"`
}

和一个返回Album

实例的函数
func GetOne(id uint64, user uint64) (Album, error) {

  var album Album

  sql := `SELECT * FROM "album" WHERE "id" = $1 AND "user" = $2;`

  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" = $1 AND "user" = $2;`, fields)

  // i don't know what to do after this

}

然后我想到将omitempty标记添加到所有字段并将字段设置为零值,然后再将其编码为JSON,

  • 会有效吗?
  • 哪一个更好?
  • 有最佳方法吗?
  • 我将如何实施第一种方法?

谢谢。

1 个答案:

答案 0 :(得分:1)

对于您的第一个提案(仅查询请求的字段),有两种方法(回答“这会起作用吗?”和“我将如何实施第一种方法?”):

  1. 使用struct动态创建一个(可能是匿名的)encoding/json并从那里生成JSON。
  2. 实现一个包装器,将您从查询中返回的*database/sql.Rows转换为JSON。
  3. 对于方法(1.),您将需要为原始struct中的任何属性组合创建struct s。作为reflect cannot create a new struct type at runtime,您唯一的机会是generate them at compile time。组合爆炸会使你的二进制文件膨胀,所以不要这样做。

    方法(2.)应谨慎处理,只能是最后的手段。获取所请求字段的列表并使用从DB获得的值写出JSON听起来很简单,并且不涉及反射。但是,您的解决方案(很可能)比encoding/json更不稳定。

    在阅读您的问题时,我也考虑过使用json:"omitempty" struct标签。而且我认为这是更好的解决方案。它既不涉及元编程,也不编写自己的JSON编码器,这是一件好事。只要注意一些字段缺失的情况(客户端可能必须考虑到这一点)。您可以查询所有属性始终并使用反射覆盖不需要的属性。

    最后,所有上述解决方案都不是最理想的,最好的解决方案是根本不实现该功能。我希望你有充分的理由让属性变量,我很乐意根据你的解释进一步澄清我的答案。但是,如果资源的某个属性太大,它可能应该是一个子资源。