api 数据返回过滤不需要的字段 - V2EX
V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
The Go Programming Language
http://golang.org/
Go Playground
Go Projects
Revel Web Framework
Wenco
V2EX    Go 编程语言

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

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

    目前在用 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  
    OP
       2020-05-17 13:00:10 +08:00
    @hq 感谢,目前用的 restful api,我了解一下
    Wenco
        4
    Wenco  
    OP
       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  
    PRO
       2020-05-17 15:41:24 +08:00
    后端处理后再返回,每一个字段都要处理,确保返回的数字是数字,字符是字符。
    另外很多时候还会返回对象,举例帖子详情接口,返回的 author 就需要是一个对象,方便展示昵称和头像
    {
    "id": 123,
    "title": "帖子标题",
    "author":{
    "id": 456,
    "name": "帖子作者昵称",
    "avatar": "帖子作者头像 URL"
    }
    }
    Wenco
        9
    Wenco  
    OP
       2020-05-17 16:29:37 +08:00
    @CEBBCAT 提问前 google 过,看你的描述应该说的和我看的那个一样,我感觉不太灵活。
    Wenco
        10
    Wenco  
    OP
       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  
    OP
       2020-05-18 08:52:24 +08:00
    @renmu 很多种情况下都会出现操作的数据对象是一样的,就是需要的字段不同,不 select * 每个地方都要写一次,代码复用超级低。其实 OOP 思想也会有类似的问题,看怎么取舍了。
    Wenco
        23
    Wenco  
    OP
       2020-05-18 08:55:48 +08:00
    @JackyCDK 前台要求不展示 password,但是后台需要 password 怎么搞?写两个不同的 struct 然后转换?
    Wenco
        24
    Wenco  
    OP
       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  
    OP
       2020-05-18 09:07:04 +08:00
    @wfhtqp 能贴一下做的比较好示例代码或示例项目吗?
    Wenco
        28
    Wenco  
    OP
       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  
    OP
       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  
    OP
       2020-05-18 10:16:27 +08:00
    @odirus 这么多人推荐 Graphql,很有诱惑力
    ViggoSite
        37
    ViggoSite  
       2020-05-18 10:22:52 +08:00
    指定 Select 字段,它不香麽?
    looplj
        38
    looplj  
       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   2
    https://colobu.com/2017/06/21/json-tricks-in-Go/
    刚好前段时间忘了在哪收藏的一篇文章,有楼主需要的
    Wenco
        43
    Wenco  
    OP
       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     Solana     1213 人在线   最高记录 6679       Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 36ms UTC 17:42 PVG 01:42 LAX 09:42 JFK 12:42
    Do have faith in what you're doing.
    ubao msn snddm index pchome yahoo rakuten mypaper meadowduck bidyahoo youbao zxmzxm asda bnvcg cvbfg dfscv mmhjk xxddc yybgb zznbn ccubao uaitu acv GXCV ET GDG YH FG BCVB FJFH CBRE CBC GDG ET54 WRWR RWER WREW WRWER RWER SDG EW SF DSFSF fbbs ubao fhd dfg ewr dg df ewwr ewwr et ruyut utut dfg fgd gdfgt etg dfgt dfgd ert4 gd fgg wr 235 wer3 we vsdf sdf gdf ert xcv sdf rwer hfd dfg cvb rwf afb dfh jgh bmn lgh rty gfds cxv xcv xcs vdas fdf fgd cv sdf tert sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf sdf shasha9178 shasha9178 shasha9178 shasha9178 shasha9178 liflif2 liflif2 liflif2 liflif2 liflif2 liblib3 liblib3 liblib3 liblib3 liblib3 zhazha444 zhazha444 zhazha444 zhazha444 zhazha444 dende5 dende denden denden2 denden21 fenfen9 fenf619 fen619 fenfe9 fe619 sdf sdf sdf sdf sdf zhazh90 zhazh0 zhaa50 zha90 zh590 zho zhoz zhozh zhozho zhozho2 lislis lls95 lili95 lils5 liss9 sdf0ty987 sdft876 sdft9876 sdf09876 sd0t9876 sdf0ty98 sdf0976 sdf0ty986 sdf0ty96 sdf0t76 sdf0876 df0ty98 sf0t876 sd0ty76 sdy76 sdf76 sdf0t76 sdf0ty9 sdf0ty98 sdf0ty987 sdf0ty98 sdf6676 sdf876 sd876 sd876 sdf6 sdf6 sdf9876 sdf0t sdf06 sdf0ty9776 sdf0ty9776 sdf0ty76 sdf8876 sdf0t sd6 sdf06 s688876 sd688 sdf86