V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
darknoll
V2EX  ›  程序员

后端传过来的某些属性不固定,有时候有,有时候没有,这样合理吗?

  •  5
     
  •   darknoll · 2021-01-26 14:49:31 +08:00 · 16057 次点击
    这是一个创建于 1402 天前的主题,其中的信息可能已经有所发展或是发生改变。

    比如和后端商定好了,返回的接口格式是: { "A": "", "B": [{}, {}] } 这时候后端说了,B 的数据有时候没有,如果没有的话就直接返回{A:""} 我让他返回{"A":"***", "B":[]}

    哪种方案好?

    187 条回复    2021-01-28 16:43:25 +08:00
    1  2  
    pandanrain123
        1
    pandanrain123  
       2021-01-26 14:50:38 +08:00
    合理
    atonku
        2
    atonku  
       2021-01-26 14:52:15 +08:00   ❤️ 4
    前端说了算,毕竟线上问题都是后台加班
    abelmakihara
        3
    abelmakihara  
       2021-01-26 14:55:37 +08:00
    后端是 map 一把梭的吗?
    uselessVisitor
        4
    uselessVisitor  
       2021-01-26 14:55:52 +08:00
    第二种逻辑上比较好,但是后台要多写逻辑处理
    opengps
        5
    opengps  
       2021-01-26 14:57:38 +08:00   ❤️ 2
    总的来说是合理的,不过为了互相方便,能提前协商就更好了。
    强制 key 存在可能需要后端额外的开销,将 null 强制初始化一个默认值
    anjianshi
        6
    anjianshi  
       2021-01-26 14:59:18 +08:00   ❤️ 6
    我是喜欢第二种,我觉得好处如下:
    - 逻辑清晰(该有的字段都有,不用另外看文档确认有哪些字段)
    - 出 bug 几率小,前端代码复杂起来不好保证所有处理这个字段的地方都妥善处理了空值,而后端只要在返回的地方处理一次就好了。
    - 测试抓包排查看数据时也清楚
    raaaaaar
        7
    raaaaaar  
       2021-01-26 15:04:20 +08:00
    并不麻烦,主要看前端的想法,之前我和一个前端合作也遇到过这个问题,我们是加上了的。
    lovecy
        8
    lovecy  
       2021-01-26 15:06:50 +08:00
    你可以要求后端给到 B:[],但是你最好适配一下没有的情况。。。
    redtea
        9
    redtea  
       2021-01-26 15:06:58 +08:00 via iPhone   ❤️ 6
    属性没有,省流量。
    lepig
        10
    lepig  
       2021-01-26 15:17:56 +08:00
    个人觉得就算为空也要返回比较合理,这样对前端开发人员比较友好。
    我一般返回之前先判断一下这个 list 是否为空,如果为空就定义一个空数组返回。
    Gehrman
        11
    Gehrman  
       2021-01-26 15:19:35 +08:00
    第二种前端更方便判断
    Takamine
        12
    Takamine  
       2021-01-26 15:30:36 +08:00 via Android
    通常会用第二种,保持结构完整性。
    lemon94
        13
    lemon94  
       2021-01-26 15:41:22 +08:00
    我是移动端,我们后端是返回{"A":"***", "B":null}
    不过对我也没什么影响,用字典转模型的三方工具直接转就可以。
    在使用的时候记得判空就好。
    wangxiaoaer
        14
    wangxiaoaer  
       2021-01-26 15:49:32 +08:00
    空也要返回,是搞笑的吗?加入一个用户对象,里面嵌套了地址等信息,难道要返回:

    {name:null,age:0,address:{postcode:null,street:null....},.....} ??

    我们一般的逻辑是:首先 null 跟空是不一样的,其次 json 序列化返回的时候自动过滤 null 值
    egoistttt
        15
    egoistttt  
       2021-01-26 15:58:44 +08:00
    第二种,其实如果用个 vo 返回,这种东西都不会很麻烦,可能只是后端偷懒,直接 map 返回给你吧
    caiqichang
        16
    caiqichang  
       2021-01-26 16:00:51 +08:00
    {A:"", B:{ C:"" }} 前端直接 B.C 没问题
    {A:"", B:null} 前端直接 B.C 没问题
    {A:""} 前端直接 B.C 报错 Cannot read property C
    caiqichang
        17
    caiqichang  
       2021-01-26 16:03:14 +08:00
    Spring Boot 默认的 jackson 没有的属性会返回 null 而不是去掉
    shaoyijiong
        18
    shaoyijiong  
       2021-01-26 16:04:28 +08:00
    我们的话如果列表为空 就是 {"a":[]} 如果字符为空就是 {"b":null}
    dengjscn
        19
    dengjscn  
       2021-01-26 16:04:56 +08:00   ❤️ 5
    @caiqichang 大哥你 JS 学的不行啊,B 为 null 咋还能 B.C 呢?
    joyhub2140
        20
    joyhub2140  
       2021-01-26 16:05:59 +08:00
    没有数据的字段,并不回返回对应的 key 了,因为大部分及格的 json 解析库都有对应的解析方法,jsonObj.opt(key, fallback),会判断如果没有对应 key 的数据,会返回 fallback 。
    caiqichang
        21
    caiqichang  
       2021-01-26 16:09:26 +08:00
    @dengjscn 所以说返回 null 前端取 B.C 不用再先判断 B 是否为 undefined,方便前端
    iugo
        22
    iugo  
       2021-01-26 16:10:28 +08:00
    根据具体业务分析, 前端可能需要额外操作, 比如 `if (res.B.length === 0) {}`.

    但如果是 B 算主业务, 则无论如何都传. 但上文的 B 应该不是主业务.
    iugo
        23
    iugo  
       2021-01-26 16:12:40 +08:00
    其实前端 `res.B ?? []` 直接解决问题. 兼容后端目前的两种可能.
    ElmerZhang
        24
    ElmerZhang  
       2021-01-26 16:16:53 +08:00
    两种方式都可以,看哪种语义上更合理一些,因为不返回 B 和返回 B:[] 是不一样的意思,不返回 B 相当于 B:null,null 值和空值的含义是不一样的。
    trlove
        25
    trlove  
       2021-01-26 16:18:32 +08:00   ❤️ 2
    个人觉得没有就直接字段都不返回。理由是:首先没有也返回,前端返回大量字段为 null 或者为空字符,不觉得难看吗 其次有些人可能有某些字段 有些人没有某些字段 但因为是一张表 全字段返回,就容易让人猜到一些关键信息字段。最后就是 难道前端展示信息的时候 对于不是必然有的字段 进行展示的时候 都不 if 一下吗 就直接用?不管数据介凡辉结构啥样,我觉得对参数要判断下是基本操作吧,万一碰上个辣鸡后端骚操作,你不判断数据直接展示,哪天突然来个字段没了啥的,然后页面报错或者啥的,直接影响用户,严重的到时候背锅的可是自己。 所以我是认为没必要返回,前端对于不是必然出现的字段进行判断,保命要紧,不能把命运交在别人手上
    killergun
        26
    killergun  
       2021-01-26 16:22:54 +08:00
    前后端可以约定好,怎么返回。
    darknoll
        27
    darknoll  
    OP
       2021-01-26 16:23:12 +08:00
    @trlove if 还是会 if 的,只是讨论接口设计
    trlove
        28
    trlove  
       2021-01-26 16:29:40 +08:00   ❤️ 2
    @darknoll 嗯嗯 我是认为没有就不返回。返回的太全,容易被人猜到核心设计。很多产品能够存活就是设计的好。全字段返回,基本一个表的字段差不多都被猜个七七八八了,有了表结构和流程原样复制产品就太简单了。再一个就是你是没碰到那种上百字段的,如果没有信息的也展示,真的吧后端搞死……还存在嵌套的问题。而前端只需要一个 if 就可解决所有字段不存在的问题……而且本身前端也得校验数据,所以相比之下我觉得不返回字段更好
    nigelvon
        29
    nigelvon  
       2021-01-26 16:33:14 +08:00
    一般来说空数组和 null 并不相等,不建议强行转换。
    jiangshanmeta
        30
    jiangshanmeta  
       2021-01-26 16:33:22 +08:00
    b 是数组 则后端应该返回空数组 b 是对象 则应返回 null 没有和 null 是两个语义
    dengjscn
        31
    dengjscn  
       2021-01-26 16:40:55 +08:00
    @caiqichang 要不你控制台试试?
    darknoll
        32
    darknoll  
    OP
       2021-01-26 16:43:01 +08:00
    @trlove 你说的也有道理,但是这样接口文档就要写仔细了,要不然原来做的人离职了,后面接手的就懵了
    Rhonin
        33
    Rhonin  
       2021-01-26 16:48:55 +08:00   ❤️ 20
    看来这个站还是后端多啊,这么奇葩的数据格式都在说合理?你们公司没有接口测试的吗?
    YouLMAO
        34
    YouLMAO  
       2021-01-26 16:59:22 +08:00
    vue 的话一般使用 protobuf, 你的 json sjb 问题不存在
    h82258652
        35
    h82258652  
       2021-01-26 17:06:34 +08:00
    要不就{"A":""},要不就{"A":"","B":null}
    反正不可能是{"A":"","B":[]}
    null 和空数组本质上就是两个不同的东西,就像空字符串和 null,存数据库表现出来的就是不一样的
    SkyLine7
        36
    SkyLine7  
       2021-01-26 17:07:14 +08:00
    第二种吧
    jzmws
        37
    jzmws  
       2021-01-26 17:08:35 +08:00
    典型的 map
    rabbbit
        38
    rabbbit  
       2021-01-26 17:09:26 +08:00
    无所谓, 反正现在可以这么写
    ```
    const a = {}
    console.log(a?.b?.c?.d || []) // []
    ```
    maemual
        39
    maemual  
       2021-01-26 17:10:57 +08:00
    支持第一种。
    不返回,或者返回 null,和返回空列表 /空字符串,语义上是不一样的。
    maemual
        40
    maemual  
       2021-01-26 17:13:07 +08:00   ❤️ 2
    @maemual #39 还有一种情况,比如产品要求上线一个功能,最后实现这个功能的数据就是 B 。如果前端、客户端一开始就支持不返回 B 就不展示这个功能的逻辑的话,未来哪一天,这个产品功能下线了,那么后端直接把字段一删,就再也不用管了。如果要求必须返回一个空列表或者空字符串,那么后端接口返回值里,始终得维护一个恶心的没用的字段。
    shoushi
        41
    shoushi  
       2021-01-26 17:13:47 +08:00
    json 转的时候可以配置 null 值是否删除字段吧
    jheroy
        42
    jheroy  
       2021-01-26 17:24:52 +08:00
    内部使用的接口直接私有协议,自己写协议文件,或者用 protobuf,这样不但性能好,还省流量。 公开协议才用 json,兼容性高,如果是公开协议了,文档肯定得写好。
    keepeye
        43
    keepeye  
       2021-01-26 17:28:03 +08:00
    前端定义个默认 json,object.assign 一下即可吧。后端可能是个动态语言,如果是结构体序列化的应该是有字段的
    keepeye
        44
    keepeye  
       2021-01-26 17:30:45 +08:00
    @Rhonin 我看淘宝接口很多这样的字段不固定,他们应该有测试吧
    fansfans
        45
    fansfans  
       2021-01-26 17:37:26 +08:00
    @Rhonin 这种数据格式前端处理起来比后端更方便 特别是对于#14 所说的情况
    Sparetire
        46
    Sparetire  
       2021-01-26 17:57:21 +08:00 via Android
    类型的角度来讲显然第二种好,第二种的类型始终是数组,处理起来更加一致。第二种情况极端情况下一个接口几十个字段一半都在判空,漏一个出了 bug 就想骂人,而那一大坨判空操作在语法糖的帮助下看起来都显得臭不可闻,没想到本站这么多人喜欢 null 这种糟粕。。
    突然觉得每个后端都应该把接口用 json schema 滤一遍,bug 从源头消灭掉
    trlove
        47
    trlove  
       2021-01-26 17:57:46 +08:00
    @Rhonin 我不知道你有没有看别人网站接口的习惯,就我观察来看,很多大厂都是第一种情况,没有字段都不返回。就比如 msg data 两个参数来说,很多大厂都是如果接口返回错误信息 那就只返回 msg 字段 看不到 data 字段了,如果业务正常返回数据 那 msg 就没了 只有 data 。而且 data 通常都是动态的,有时候可能是单条数据 有时候可能是单字符 有时候可能是数组,按第二种方式来,如果 data 为空,没数据的时候,那这个 data 是给它赋值空字符 还是空数组?还是给它 null?其次有些字段用不到的返回有什么意义呢,显得 json 长吗?而且一般这种处理都是后端通用处理方式,不会是每个接口单独去处理到底该返回空字符还是 null 还是空数组问题,单个接口都来一遍,碰到字段多的,嵌套多的,得写多少个循环多少个 if……
    znyq2019
        48
    znyq2019  
       2021-01-26 17:58:42 +08:00   ❤️ 2
    两边都做 . 别想着省事
    zeropercenthappy
        49
    zeropercenthappy  
       2021-01-26 18:05:33 +08:00
    作为一个移动端开发,我支持第一种,数据看起来舒服,处理起来也不需要太多额外的操作。
    可以省略,但是不能变。
    如果一会儿是"B":[],一会儿是"B":"",那我就要喷人了。
    laminux29
        50
    laminux29  
       2021-01-26 18:20:23 +08:00
    必然第二种方案,因为人都会犯错,第一种方案无法区分到底是 B 不存在,还是前端忘了回发 B 的值。

    另外:B 不存在、B 存在但为空、B 存在不为空但为空数组等等,这些都是不同的逻辑。建议增加状态字段来区分这些情况。

    比如前端应该这样返回:

    struct ResultData
    {
    ....string A;

    ....//Bstate:B 的状态码
    ....// 0:B 不存在; 1:B 存在但为 null ; 2:B 存在且有值。
    ....byte Bstate;

    ....//当 Bstate 为 2 时,允许 B 为空数组。
    ....array B;
    .
    }
    guanhui07
        51
    guanhui07  
       2021-01-26 18:26:38 +08:00
    第二种
    trlove
        52
    trlove  
       2021-01-26 18:28:11 +08:00
    @laminux29 加状态字段……然后一个接口 100 字段,外加 100 个状态字段,然后还得根据状态字段去决定是否渲染值…… 那为何不直接 if 字段 然后渲染…… 至于说的无法区分到底是 B 不存在还是前端忘了回发 B 的值,对于第二种,如果这个错误能犯,那你家状态字段,也可能会犯不判断状态的错误……这种问题就是自己的 bug 。对于第一种,只要你前端判断没有这个字段,至于后台是真有还是真没有,那是后端的事,出问题也是后端吧锅端着
    cco
        53
    cco  
       2021-01-26 18:33:53 +08:00
    你的逻辑需要都有,那么是空值那就得都有,你的逻辑有判断,那就没啥毛病。
    Paladinfeng
        54
    Paladinfeng  
       2021-01-26 18:37:36 +08:00
    数组为空最好给个空数组,要不前端用起来不好用
    IvanLi127
        55
    IvanLi127  
       2021-01-26 18:38:33 +08:00 via Android
    第二种方案好,合理。第一种方案为了性能?为了性能为什么还用 json ?就是懒!
    AoEiuV020
        56
    AoEiuV020  
       2021-01-26 19:17:44 +08:00
    我也觉得数据类型必需统一,有数据是数组,那没数据当然就是空数组了,你敢给 null 我就敢崩溃,
    koolob
        57
    koolob  
       2021-01-26 19:30:15 +08:00
    第一种呀。要知道出流量可都是钱呀。
    gzf6
        58
    gzf6  
       2021-01-26 19:33:19 +08:00
    data?.B
    galikeoy
        59
    galikeoy  
       2021-01-26 19:47:39 +08:00
    这个站还是后端多啊,s 逼数据结构,后面接手的人一脸懵逼
    @Rhonin #33
    ashmodeus
        60
    ashmodeus  
       2021-01-26 19:52:42 +08:00
    最好自己写兼容逻辑,凡是提前协商的在真正出线上问题时,都有可能变成甩锅的情况。
    nicevar
        61
    nicevar  
       2021-01-26 20:31:18 +08:00   ❤️ 1
    第一种是合理的,早期在做手机应用的时候还是 wap 时代,那个时候的后端不可能按第二种给你传,本来稍微大点数据都得拆,你一堆空字段还传下来不被喷死。如今虽然网络快了,但是也架不住一大堆网站下发一堆垃圾数据,20 个字段有 5 个是必选的,其他的可选下发一堆空的,真的很蠢。
    asAnotherJack
        62
    asAnotherJack  
       2021-01-26 20:38:22 +08:00
    楼上好多说用的是 map,不见得,go 里面 list ( slice )零值就是 nil,不做处理 json 就是 null,加上 tag omitempty 就是直接不带零值的字段了。至于没值还要返回[],我个人是不乐意做的。
    ichou
        63
    ichou  
       2021-01-26 20:47:30 +08:00 via iPhone
    graphql
    laminux29
        64
    laminux29  
       2021-01-26 21:01:27 +08:00
    @trlove

    1.如果接口有 100 字段,那么外加 100 个状态字段是合理的。根据状态字段去进行判断是否要进行渲染,这也是合理的。

    2.如果加了状态字段,前端犯了不判断状态的错误,那么很显然这里就只存在这一种错误了,这种错误容易发现,容易排查。但不加状态字段,错误的原因会更多,需要一条条去排查,费时费力。

    总之,这种设计是加强鲁棒性的做法,你们这些萌新需要仔细体会,加强学习。
    yazinnnn
        65
    yazinnnn  
       2021-01-26 21:04:12 +08:00
    b 没数据就返回空集合啊。。。
    你们商量好就行
    agdhole
        66
    agdhole  
       2021-01-26 21:11:08 +08:00
    前端把 es 开到最新,用 b?. 来取值(逃
    Actrace
        67
    Actrace  
       2021-01-26 21:26:38 +08:00
    工程化的项目中,基本不存在的协商这种说法。
    后端按产品需求考虑所有可能的情况,设计接口,然后文档化。
    前端按照文档进行开发,各司其职,没有什么可以扯皮的地方。
    fiypig
        68
    fiypig  
       2021-01-26 21:36:30 +08:00 via iPhone
    不管有没有,前端就几个判断吧,不然你那边很容易报错
    gimp
        69
    gimp  
       2021-01-26 21:36:50 +08:00
    第二种,没数据应该返回空数组,保证字段的类型相同,能减少前端很多不必要的判断,同时也有利于接口自动化测试。

    之前调试几个接口,有时候没有字段,有时候回个 null,有时候回空数组,这种不稳定的接口真是一言难尽。
    DreamSpace
        70
    DreamSpace  
       2021-01-27 00:36:51 +08:00   ❤️ 2
    个人理解这要根据业务逻辑来决定。
    {"A":null} 代表属性 A 没有对应的数据。
    {"A":[]} 代表属性 A 有对应的数据,但数据为空。
    具体采用哪种交互得根据业务的真实逻辑来,而不是哪种方便用哪种。
    lizytalk
        71
    lizytalk  
       2021-01-27 00:54:08 +08:00 via iPhone
    也不是不合理。但是得确定好哪些是可能没有的,哪些一定有
    TheWidowMaker
        72
    TheWidowMaker  
       2021-01-27 01:00:45 +08:00 via Android
    前后端打架现场…
    lpbname777
        73
    lpbname777  
       2021-01-27 01:45:27 +08:00
    @gimp 最烦这种的
    lpbname777
        74
    lpbname777  
       2021-01-27 01:47:10 +08:00   ❤️ 1
    用接口就只有一条规则,数据结构要稳定。Array/String 类型的字段不能返回 null,不能不返回
    lpbname777
        75
    lpbname777  
       2021-01-27 01:50:55 +08:00
    @znyq2019 或者就像这老哥说的,都校验 /处理,谁也别跑
    kassadin
        76
    kassadin  
       2021-01-27 02:16:43 +08:00   ❤️ 5
    大家能沟通就文档约定,不能沟通就防御型编程,主动降低背锅率。
    OHyn
        77
    OHyn  
       2021-01-27 02:34:51 +08:00
    都行,要明确好就行。
    别 Swagger 上面写了返回 Array,到时候返回 null 或直接没属性。
    linmq
        78
    linmq  
       2021-01-27 05:35:32 +08:00 via iPhone
    我倾向第二种
    jdlau
        79
    jdlau  
       2021-01-27 08:20:44 +08:00 via Android
    看到有大量 null 的情况,我有点好奇,这样的设计有没有问题呢?
    lplk
        80
    lplk  
       2021-01-27 08:30:18 +08:00 via Android
    @trlove 上百字段这个我太有体会了,做转换,有的嵌套列表,没有 key 也加上,就很恶心
    tsanie
        81
    tsanie  
       2021-01-27 08:35:04 +08:00
    第一种第二种都行,但是就算是第二种方案,前端最好也要做一下 B 没有的容错吧
    wangyzj
        82
    wangyzj  
       2021-01-27 08:37:17 +08:00
    为空也得返回,减少 confuse
    lrh3321
        83
    lrh3321  
       2021-01-27 08:46:11 +08:00 via Android
    都合理,互相不信任防御性编程,到时候不容易出错
    whileFalse
        84
    whileFalse  
       2021-01-27 08:48:32 +08:00
    首先,前端能有多难处理一个不存在的属性?
    其次,要说合理的话还是要具体分析。有的业务情况下,不存在和存在一个空数组是不同的状态,需要分别处理的。
    mmdsun
        85
    mmdsun  
       2021-01-27 08:49:11 +08:00 via Android
    对象空可以返回 null
    数组空建议是返回空 []
    john6lq
        86
    john6lq  
       2021-01-27 08:52:16 +08:00 via iPhone
    说返回 null 合理的不怪你写一辈子烂代码。
    netnr
        87
    netnr  
       2021-01-27 08:52:17 +08:00 via Android
    不合理
    xianxiaobo
        88
    xianxiaobo  
       2021-01-27 08:52:22 +08:00
    肯定是第二种啊,第一种容易出问题,必须让后端改成第二种,不然离职。
    guisheng
        89
    guisheng  
       2021-01-27 09:01:54 +08:00 via iPhone
    如果没有接口文档建议写第二种,如果有我建议使用第一种。
    ebony0319
        90
    ebony0319  
       2021-01-27 09:03:49 +08:00
    我猜其实后端也一脸懵逼.序列化框架做的.第一种目前也存在,只是用的少了而已.
    GoodKvm
        91
    GoodKvm  
       2021-01-27 09:04:01 +08:00
    第二种
    p1gd0g
        92
    p1gd0g  
       2021-01-27 09:05:05 +08:00
    用 protobuf 吧。。。
    ramcasky
        93
    ramcasky  
       2021-01-27 09:06:32 +08:00
    @whileFalse 业务复杂还挺难处理的
    weixiangzhe
        94
    weixiangzhe  
       2021-01-27 09:08:56 +08:00
    主要是 null 前端不是很好处理,建议 request 那里处理一下,为 null 改为 undefined
    roudancongji
        95
    roudancongji  
       2021-01-27 09:10:05 +08:00
    盲猜一手用的 mongodb ?
    lagoon
        96
    lagoon  
       2021-01-27 09:11:21 +08:00
    有多少公司能很好的维护更新文档的?
    反正我见过许多老项目,最终都是要看代码来理解的。

    {A:""},以后想要知道这接口只返回 A,还是可能会有 B,就比较麻烦了。特别是一些不怎么出现的参数。
    接口按结构返回了,以后调一遍,看看结构,理解当时的接口情况简单多了。
    EminemW
        97
    EminemW  
       2021-01-27 09:13:03 +08:00 via iPhone
    怎么可能返回 null……真有人这么写?
    yaphets666
        98
    yaphets666  
       2021-01-27 09:16:04 +08:00   ❤️ 7
    觉得第一种好的,不论你干了多少年,源码多熟算法多牛逼,你的同事一定讨厌你.
    luhuisicnu
        99
    luhuisicnu  
       2021-01-27 09:16:06 +08:00
    看场景吧,非必要的可选字段,前端检查到就展示,未检查到就不做处理,如果是必须字段,那么必须要返回 key,value 看场景给出
    sonxzjw
        100
    sonxzjw  
       2021-01-27 09:16:42 +08:00
    从更好性能、更少重复工作来看,b 的提议更好。
    1 是少传数据,2 是只要前端进行判断(可能不需要判断)
    如果用 2 的方案,数据多传,后端还可能要进行判断来输出 b:[](也可能不需要),前端也要进行空判断(也可能不需要)

    情况写明白了,那客观上选哪个方案一目了然了。主观的话就不谈了。
    1  2  
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   2945 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 40ms · UTC 15:02 · PVG 23:02 · LAX 07:02 · JFK 10:02
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.