V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
beego
Wenco
V2EX  ›  Go 编程语言

api 数据返回过滤不需要的字段

  •  
  •   Wenco · 2020-05-17 12:41:15 +08:00 · 5289 次点击
    这是一个创建于 559 天前的主题,其中的信息可能已经有所发展或是发生改变。

    目前在用 gin+gorm 写一个小玩意,遇到这样的情况:

    为了代码复用及写起来方便,基本都是用的 orm select *

    有时候面向前台的接口不方便展示数据集中的某个字段,但是面向后端的又需要

    这应该是前后端分离都会遇到的吧,请教各位大佬,比较好的解决方案是什么?

    第 1 条附言  ·  2020-05-18 10:23:03 +08:00

    大家误会了,这是我业余写的小玩意

    还在写的过程中,没有将查询到的所有数据直接返回给前端

    提问是为了找一个更好的解决方案。

    44 条回复    2020-05-21 14:41:08 +08:00
    hq
        1
    hq   2020-05-17 12:46:31 +08:00 via Android
    heimeil
        2
    heimeil   2020-05-17 12:53:55 +08:00
    type User struct {
    Name string `json:"name"`
    Password string `json:"-"`
    }
    Wenco
        3
    Wenco   2020-05-17 13:00:10 +08:00
    @hq 感谢,目前用的 restful api,我了解一下
    Wenco
        4
    Wenco   2020-05-17 13:00:46 +08:00
    @heimeil 重写一个结构体,然后将查询出来的 model 数据转移吗。
    kasper4649
        5
    kasper4649   2020-05-17 13:16:16 +08:00 via Android
    DTO ?
    pubby
        6
    pubby   2020-05-17 13:26:42 +08:00 via Android   ❤️ 2
    var u User =.....

    //举例:隐藏 User 里的 password

    var out = struct{
    User
    Hide1 bool `json:"password,omitempty"`
    }{
    User: u,
    }

    json.Marshal(out)

    //原理:利用 omitempty 属性的空值同名字段覆盖
    CEBBCAT
        7
    CEBBCAT   2020-05-17 13:48:38 +08:00 via Android
    这个 Google 一下就有了吧。可以重新定义结构体的 JSON 相关方法
    qiayue
        8
    qiayue   2020-05-17 15:41:24 +08:00
    后端处理后再返回,每一个字段都要处理,确保返回的数字是数字,字符是字符。
    另外很多时候还会返回对象,举例帖子详情接口,返回的 author 就需要是一个对象,方便展示昵称和头像
    {
    "id": 123,
    "title": "帖子标题",
    "author":{
    "id": 456,
    "name": "帖子作者昵称",
    "avatar": "帖子作者头像 URL"
    }
    }
    Wenco
        9
    Wenco   2020-05-17 16:29:37 +08:00
    @CEBBCAT 提问前 google 过,看你的描述应该说的和我看的那个一样,我感觉不太灵活。
    Wenco
        10
    Wenco   2020-05-17 16:32:32 +08:00
    @pubby 嗯,感觉这个挺好的
    JackyCDK
        11
    JackyCDK   2020-05-17 18:22:15 +08:00
    json: - , binding: password,required
    renmu
        12
    renmu   2020-05-17 18:25:34 +08:00 via Android
    都是 select * 会被骂的吧😂😂性能有问题
    securityCoding
        13
    securityCoding   2020-05-17 19:55:33 +08:00
    @renmu 表设计好,索引用好并不会有什么问题...
    GM
        14
    GM   2020-05-17 23:03:40 +08:00
    很简单,换 PHP 就好了!(* ̄ rǒ ̄)
    winrar
        15
    winrar   2020-05-18 01:21:44 +08:00
    这个 ORM 都有的吧,比如 django 的 values(fields, *expressions)
    winrar
        16
    winrar   2020-05-18 01:26:32 +08:00
    @winrar 看错了,此条无视
    ppphp
        17
    ppphp   2020-05-18 02:04:26 +08:00
    我的实践上还是返回的 model 和数据表的 model 会转一下,方便点,改表也没有心理压力
    baobao1270
        18
    baobao1270   2020-05-18 05:04:11 +08:00
    .NET 有 DTO,其他语言应该也有类似的,思路就是用中间类转换一下
    k9982874
        19
    k9982874   2020-05-18 07:31:14 +08:00 via iPhone
    先不说 select *的问题,你是把数据直接怼进 map 里?
    Counter
        20
    Counter   2020-05-18 07:35:14 +08:00 via Android
    创建一个专门用于输出数据的类,不要直接输出实体
    wfhtqp
        21
    wfhtqp   2020-05-18 08:41:51 +08:00
    使用 tag+reflect,根据请求 fields 返回需要的字段
    Wenco
        22
    Wenco   2020-05-18 08:52:24 +08:00
    @renmu 很多种情况下都会出现操作的数据对象是一样的,就是需要的字段不同,不 select * 每个地方都要写一次,代码复用超级低。其实 OOP 思想也会有类似的问题,看怎么取舍了。
    Wenco
        23
    Wenco   2020-05-18 08:55:48 +08:00
    @JackyCDK 前台要求不展示 password,但是后台需要 password 怎么搞?写两个不同的 struct 然后转换?
    Wenco
        24
    Wenco   2020-05-18 08:57:48 +08:00
    @k9982874 https://gorm.io/docs/query.html 不需要用 map 啊,创建好表 struct 就行了
    useben
        25
    useben   2020-05-18 09:06:10 +08:00
    dto vo po
    reus
        26
    reus   2020-05-18 09:06:25 +08:00
    如果出什么 bug,密码泄露到前台,你就知错了。

    好的设计,就算出现 bug,也不至于出现敏感数据泄露。

    安全距离是很重要的,你这种做法,不安全。
    Wenco
        27
    Wenco   2020-05-18 09:07:04 +08:00
    @wfhtqp 能贴一下做的比较好示例代码或示例项目吗?
    Wenco
        28
    Wenco   2020-05-18 09:08:38 +08:00
    @reus 正在写的过程中,我知道这样做肯定是不好的,所以来这里寻求比较好(方便,高效)的解决方案
    dandycheung
        29
    dandycheung   2020-05-18 09:10:21 +08:00 via iPhone
    一楼正解。
    siteshen
        30
    siteshen   2020-05-18 09:45:34 +08:00
    // models/user.go
    struct User {
    username string
    password stirng
    age int
    ...
    }

    // responses/user.go
    struct UserResponse {
    username string
    age int
    ...
    }

    func NewUserResponse(user *User) {
    ...
    }

    // handlers/user.go
    funct ReadUserHandler(req, res) {
    user = models.ReadUserById(req.UserId)
    return response.NewUserResponse(user)
    }
    siteshen
        31
    siteshen   2020-05-18 09:49:37 +08:00
    @siteshen 简单的说,就是你们少了一层抽象:API response 。加上之后 API 返回字段就只和 API response 层相关,不需要和 model 、logic 层耦合(甚至 model 用 ORM 实现、甚至存储换成 redis/mongodb 也不会影响前端)。
    wfhtqp
        32
    wfhtqp   2020-05-18 09:57:06 +08:00
    Wenco
        33
    Wenco   2020-05-18 10:00:21 +08:00
    @siteshen 是不是可以理解为 response 就是定义数据返回格式的,有几处不同的返回格式就定义几个 response,NewRespons 没有实际输出,只是进行数据重组?
    odirus
        34
    odirus   2020-05-18 10:10:34 +08:00
    举个例子,假设需要分别返回“课程信息”给 PC 端和 APP 端,他们需要返回的字段详细程度是不一样的,可以这样做

    查询课程 ID -> 查询出该课程的详细信息 -> 缓存到某个地方(例如 ES ) -> 通过 Graphql 分别取各个端需要的字段信息 -> 返回给端

    更新课程 ID -> 更新 DB 中的课程信息 -> 更新 ES 中的课程信息
    Pythondr
        35
    Pythondr   2020-05-18 10:13:08 +08:00
    不建议将数据库的 model 直接返回, 一般会定义一个 controller 层的 model, 这样的数据字段更具有灵活性,也更安全
    Wenco
        36
    Wenco   2020-05-18 10:16:27 +08:00
    @odirus 这么多人推荐 Graphql,很有诱惑力
    ViggoSite
        37
    ViggoSite   2020-05-18 10:22:52 +08:00
    指定 Select 字段,它不香麽?
    ZSeptember
        38
    ZSeptember   2020-05-18 10:24:35 +08:00
    VO,DTO,各种结构职责分离
    misaka19000
        39
    misaka19000   2020-05-18 10:25:58 +08:00
    添加 exclude 或者 include 选项
    pena
        40
    pena   2020-05-18 15:05:35 +08:00
    https://github.com/pengwenwu/gin-items

    不介意的话可以看下我自己写的小项目,基于 gin 的 restfull 风格的商品服务。

    不定字段可以通过 scan 转到 map 里,不过性能会有影响
    siteshen
        41
    siteshen   2020-05-18 16:29:09 +08:00
    @Wenco NewUserResponse 是其他语言中的构造函数,这样对生成的 UserResponse 可控(甚至 UserResponse 可以设计为 private 的 userResponse )。

    UserResponse 也是可以复用的,下面这个例子就会复用 BasicUser 。
    用户列表可能就只需要 BasicUser (用户名、头像),
    查看用户详情时可能需要 UserDetail (BasicUser + 粉丝数、文章数),
    查看自己的详情时可能需要 Me ( UserDetail + 收入 + 草稿箱中的文章数)。


    struct BasicUser {
    username string
    avatar url
    }

    // API readUser
    struct UserDetail {
    UserResponse
    bio string
    location string
    n_follower int
    n_following int
    n_posts int
    }

    // 用户查看自己信息的时
    struct Me {
    UserDetail
    n_fav_posts int
    total_cost double
    total_income double
    n_draft_posts int
    }

    // API listUser
    struct UserList {
    users BasicUser[]
    }
    asAnotherJack
        42
    asAnotherJack   2020-05-19 11:37:03 +08:00
    https://colobu.com/2017/06/21/json-tricks-in-Go/
    刚好前段时间忘了在哪收藏的一篇文章,有楼主需要的
    Wenco
        43
    Wenco   2020-05-19 13:32:46 +08:00
    @asAnotherJack 感谢,很有用!
    JackyCDK
        44
    JackyCDK   2020-05-21 14:41:08 +08:00
    其实对 password 字段 omitempty 然后在 response 时直接给它赋值为空就完事了...我
    @JackyCDK #11 的回复似乎是有问题的,我当时没查证,抱歉。
    关于   ·   帮助文档   ·   API   ·   FAQ   ·   我们的愿景   ·   广告投放   ·   感谢   ·   实用小工具   ·   1532 人在线   最高记录 5497   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 27ms · UTC 17:20 · PVG 01:20 · LAX 09:20 · JFK 12:20
    ♥ Do have faith in what you're doing.