V2EX = way to explore
V2EX 是一个关于分享和探索的地方
现在注册
已注册用户请  登录
• 请不要在回答技术问题时复制粘贴 AI 生成的内容
laaaaaa
V2EX  ›  程序员

客户端不接收 null, 各位服务端大佬都是怎么优雅处理的?

  •  
  •   laaaaaa · 2024-01-29 16:12:07 +08:00 · 4893 次点击
    这是一个创建于 373 天前的主题,其中的信息可能已经有所发展或是发生改变。

    环境

    jdk8, springboot;
    

    需求

    客户端包括了很多,电视端 手机端 小程序端 web 端;
    
    除了 web 端,好像都不太愿意接收 null ;
    
    例如 java 一个实体类 我返回给他们,某些字段为空的话 也想接收 "",不要 null ;
    
    
    这一块我想问问大佬们,都是怎么优雅处理的;
    
    不会要每个 bean ,都 private String a = "";
    

    ps

     1. 判断 Null  和 "" 有很大区别吗? 因为什么呢
     2.java 编译过程中,null 要比""占用的资源少吧
    
    第 1 条附言  ·  2024-01-29 18:58:20 +08:00
    统一回复一下:

    序列化处理应该是可以的,@JsonInclude(JsonInclude.Include.NON_NULL)

    但是有几个点

    1.这个序列化,我在每个返回类上都要加这个 这个感觉好别扭- -

    2.我加上这个注解之后,会不会跟其他的序列化冲突啊,例如 这个返回类 我要缓存到 redis, redis 用的 fastjson 做的序列化;

    3.除了返回给客户端 为 null 的不反悔, 我在我的业务功能里 为 null 的时候 我也需要有这个字段,这也会有异常呀
    73 条回复    2024-02-01 14:54:03 +08:00
    doco
        1
    doco  
       2024-01-29 16:12:56 +08:00   ❤️ 8
    web 端也不想要 null
    ZhLTE
        2
    ZhLTE  
       2024-01-29 16:16:05 +08:00
    序列化的时候统一处理
    pota
        3
    pota  
       2024-01-29 16:18:39 +08:00
    这么和你说 "" 和 null 不是一个类型的数据。你定义一个参数是 string 我塞一个 null 给你,你怎么想?
    laaaaaa
        4
    laaaaaa  
    OP
       2024-01-29 16:19:25 +08:00
    @pota 大佬是怎么解决这个问题的
    pota
        5
    pota  
       2024-01-29 16:20:07 +08:00   ❤️ 1
    @pota #3 手快发布了。这就不是麻不麻烦的事,作为一个 API 。保持数据格式一致是最基础的要求,还有 json 的{}没数据给[] 的时候也很恶心。
    falsemask
        6
    falsemask  
       2024-01-29 16:20:43 +08:00
    spring.jackson.default-property-inclusion=NON_NULL
    laaaaaa
        7
    laaaaaa  
    OP
       2024-01-29 16:22:36 +08:00
    @pota 对 还有这个问题,如果返回的是 json 或者 数组, 没有数据 我一般会给 [] 或者 {}, 但是服务端如果返回这样 貌似要 new 一个对象返回, 这不是又耗费资源了吗
    me1onsoda
        8
    me1onsoda  
       2024-01-29 16:24:23 +08:00
    null 要多一步判空吧
    chendy
        9
    chendy  
       2024-01-29 16:25:14 +08:00
    直接不返回就行
    或者让他们换一个完善点的 ui 框架,自动把 null 显示成空白那种
    azhangbing
        10
    azhangbing  
       2024-01-29 16:26:12 +08:00
    因为 kotlin 默认非空
    wu67
        11
    wu67  
       2024-01-29 16:27:17 +08:00
    举个例子.
    你这样返回的时候, JS 代码仔需要写多好几个判断来取数, 尤其是需要字符串内容做正则的时候
    if (typeof a === 'string' && /balabala/.test(a)) {}
    要不就是 if (a.length) 能解决的时候, 需要 if (Array.isArray(a) && a.length) , 看起来是没什么, 但是页面结构复杂度以上去, 数据字段一开始多, 这一大堆多出来的判断, 就会成为客户端代码的屎山
    NewYear
        12
    NewYear  
       2024-01-29 16:30:39 +08:00   ❤️ 2
    1 、有的语言里,null 会报错。还会导致 if 判断不按预期的分支走。(特别适合用来写屎山,因为从语言层面你看不出任何一点不按预期分支走的可能,直到单独调试在这里才发现卧槽)
    2 、有的语言里,null 不会报错但是每次都要考虑它就显得很烦了,谁都不想增加负担。

    一般的解决方案:禁止 null 的存在,明确每个值的意义。

    未定义也不许用 null ,字符串可以用"",数字可以用 0 或者-1 (具体按自己的定义来)。
    数字类型:1 是男,0 是女,-1 是未定义
    又或者 0 是未定义,1 是男,2 是女,3 是跨性别男,4 是……

    字符串类型:""是未定义,"男"是男。

    日期类型:“1970 年 1 月 1 日”是未定义,或者“0000 年 00 月 00 日”是未定义。
    MoonLin
        13
    MoonLin  
       2024-01-29 16:32:20 +08:00
    不返回的话他们可以自己用默认值,返回为 null 就覆盖了默认值,如果要处理的话其实也可以,自己实现反序列化就好了,但是我估计客户端不愿意干,不然你也不会来问了。所以你不如直接不返回,看你用什么序列化框架,一般如 6#所述加个注解就好了。
    NewYear
        14
    NewYear  
       2024-01-29 16:34:27 +08:00
    null 和 string/int/date 的类型都不同,不能直接用真的很烦。

    楼主后端用是什么语言,不是强类型语言吗。
    sanmaozhao
        15
    sanmaozhao  
       2024-01-29 16:36:49 +08:00
    我觉得应该返回 null ( OP 没说返回的数据格式是啥,默认 JSON 了)

    1 、JSON 里面本来就是有 null 这个类型的
    2 、数字型,不返回 null 的话,用什么表示这项数据没有值呢?用哪个数值都不太合适
    如果没值的项直接不返回的话,其实和返回 null 差别也不大
    比如对于 js ,就是判断 undefined 和 null 的差别
    wu67
        16
    wu67  
       2024-01-29 16:39:22 +08:00
    还有一个典中典的, 忘记是什么时候遇到的, 有个变量的值, 是 'null'...

    出 bug 找前端查的时候, 那一个山崩地裂...

    if('null') {1} else {2}
    // 1
    null123456
        17
    null123456  
       2024-01-29 16:40:26 +08:00
    调用此方法:StringUtils.isNotBlank(String str)
    vagusss
        18
    vagusss  
       2024-01-29 16:42:34 +08:00
    springboot 默认的序列化应该是 Jackson, 应该有配置项可以直接达到你要的效果
    lyxxxh2
        19
    lyxxxh2  
       2024-01-29 16:43:53 +08:00
    啥类型的就返回啥类型 很正常
    web 端口也一样
    字符: ''.split('a')
    数组: [].map((() => {})如果返回个 null,前端: mmp
    cslive
        20
    cslive  
       2024-01-29 16:44:12 +08:00
    加注解,字段为 null 会忽略 json 序列化
    yxisenx
        21
    yxisenx  
       2024-01-29 16:47:41 +08:00
    jackson 的话直接在返回实体上面加个
    @JsonInclude(JsonInclude.Include.NON_NULL)
    potatowish
        22
    potatowish  
       2024-01-29 16:52:06 +08:00 via iPhone
    返回空字符串和 null 在服务端含义是不同的,而客户端是表现层,所以这个差异是体现不出来的,都是没有值。

    还有就是代码健壮性的问题,如果客户端处理了 null ,那么即便服务端返回空字符串,客户端也不会有问题。
    nicocho
        23
    nicocho  
       2024-01-29 16:52:22 +08:00 via Android
    好久没看到优雅这个词了
    sanmaozhao
        24
    sanmaozhao  
       2024-01-29 16:56:01 +08:00
    @potatowish
    > 返回空字符串和 null 在服务端含义是不同的,而客户端是表现层,所以这个差异是体现不出来的,都是没有值。

    这个其实不太对,现在的客户端早就不是简单的渲染表现层了,也是有部分业务逻辑在里面的
    所以我上面才觉得,服务端就应该如实地返回 null
    XXWHCA
        25
    XXWHCA  
       2024-01-29 17:00:35 +08:00
    null 不返回就行,他们可能是不喜欢返回没有意义的 null 字段,客户端这种强类型语言开发时不返回的字段引用类型默认值也是 null😂,文档写明白就行了
    kalista
        26
    kalista  
       2024-01-29 17:24:24 +08:00
    go 也不喜欢 null ,甚至数据库定义里面都是 not null 的
    Ayanokouji
        27
    Ayanokouji  
       2024-01-29 17:27:57 +08:00
    数据库加默认值,能解决不少问题
    yolee599
        28
    yolee599  
       2024-01-29 17:28:25 +08:00   ❤️ 1
    为啥要返回 null ?没意义啊,如果这个字段没有数据,直接不传这个字段就好了,不用多此一举还浪费网络带宽。
    treblex
        29
    treblex  
       2024-01-29 17:29:02 +08:00
    我最近开始写 getValue(obj,"a.b.c.d") 这样的东西,取到 null 的时候就不会崩溃了🙈
    ZeroDu
        30
    ZeroDu  
       2024-01-29 17:34:22 +08:00
    正确操作是直接不序列化这个字段。别返回空字符,这种才是
    wangritian
        31
    wangritian  
       2024-01-29 17:35:01 +08:00
    我是 go 后端,用中间件拦截控制器的输出,然后通过反射递归的把空指针 nil 改成空值
    但其实站在团队角度,我更支持客户端应该有健壮性,兼容 null 和空值
    就像我从来不设计请求参数不传(null)和传空值有不同含义,应该最大的去兼容
    nerkeler
        32
    nerkeler  
       2024-01-29 17:42:12 +08:00
    `private String a = ""` 简单粗暴,或者用注解 @JsonIgnore 指定参数(具体忘记是啥了) 不返回 null 的字段 ,怎么简单怎么来呗,或者统一处理请求字段 null 的都给个初始值,不过实现麻烦了点
    broken123
        33
    broken123  
       2024-01-29 18:13:35 +08:00
    你应该想想各个客户端的语言是不一样的 安卓 java kotlin ,flutter dart web js ts , 还有各种小程序 直接给一个默认值就行了
    broken123
        34
    broken123  
       2024-01-29 18:16:17 +08:00
    实际上客户端也只有强制类型语言才会 做 null 判断 js 会 出现 undfi 和 null 这种情况 java 写他妈一大堆非空判断再取是真的很烦心。代码丑陋的一笔。
    darkengine
        35
    darkengine  
       2024-01-29 18:27:46 +08:00
    有一天,后端跟我说,前端要判断某个接口返回的复杂结构里字段是不是 null 或者 undefine

    我问哪个字段需要判断,他说不是很简单吗,所有字段。。。

    卧尼🐎个很简单
    zjp
        36
    zjp  
       2024-01-29 18:34:38 +08:00
    在序列化层做呗,Jackson 的话是单独处理 null 值的,参考 https://stackoverflow.com/a/18123772
    JsonInclude.Include.NON_NULL 的结果是没有字段
    ooo4
        37
    ooo4  
       2024-01-29 19:11:23 +08:00
    前端加一大段```a?.b?.c?.d ?? "无数据"```
    zjsxwc
        38
    zjsxwc  
       2024-01-29 19:23:03 +08:00 via Android
    我写 js 就喜欢 null ,

    如何某个字段是 object , 但这个 object 数据没有,那你也必须给我一个 null 值,不然如果你直接不给我这个字段,我 js 取这个字段时就直接报错执行不下去。
    zjsxwc
        39
    zjsxwc  
       2024-01-29 19:25:58 +08:00 via Android
    我写 js 就喜欢 null ,

    如果某个字段是 object , 但这个 object 数据没有,那你也必须给我一个 null 值,不然如果你直接不给我这个字段,我 js 取这个字段时就直接报错执行不下去。

    如果某个字段是 int ,你却给了我一个 0 ,我怎么表示,这是业务上比如用户还没填写,还是用户已经填写了 0 ,string 也一样道理。
    xuanbg
        40
    xuanbg  
       2024-01-29 21:01:19 +08:00
    null 就是 null 。又不是我要给 null ,肯定是你前端没给数据数据库里面才存的 null 。你前端凭啥不要 null ?
    mshadow
        41
    mshadow  
       2024-01-30 00:27:34 +08:00 via Android
    @pota {}给[],一般是后端 php 的坑,map 和数组在 php 中都是 array ,空 array 会 json 序列化为[],这个一般要 php 特殊处理一下
    oatw
        42
    oatw  
       2024-01-30 08:52:37 +08:00
    有一种主义叫“无 null 主义”,数据库里就不可以有 null ,尤其是涉及到三值逻辑的时候。
    DzwsGo
        43
    DzwsGo  
       2024-01-30 09:02:45 +08:00
    kotlin 默认非空,如果没有做特殊处理 json 转换的时候遇到 null 就会崩溃,做处理就会出现很多?
    另外
    @chendy #9 后端是不是可以换个完善点的框架,自动把 null 换成相应数据类型默认值? 😁
    pota
        44
    pota  
       2024-01-30 09:14:29 +08:00
    @laaaaaa #7 java 应该没有这个问题吧? map 结构化之后就是{}啊。像楼上说的,PHP 会有这个问题。API 数据类型一致因为我不是 java ,一般我解决方案就是可能出现类型不一致的定义 Attribute 获取
    cleveryun
        45
    cleveryun  
       2024-01-30 09:32:13 +08:00 via Android
    前面有楼层提到数值没值返回 0 不合适时返回 null 的。这个没业务场景的情况下且认为没问题。这里其实他们说的最大问题不是是否返回 null ,而是与接口文档是否一致。如果会返回 null ,那接口文档里就不应该只写会返回数值,应该把 null 也写上。这是联调涉及到的契约精神的问题,不应该字段值类型都变成盲盒。
    chendy
        46
    chendy  
       2024-01-30 09:37:20 +08:00
    @DzwsGo 写过 kt 和 ts ,需要?的时候就给?呗,业务逻辑允许 null ,代码没理由不允许 null (其实展示层还好,如果有逻辑啥的就比较麻烦,有时候甚至原样复制粘贴但是去掉?然后加校验和报错)
    这么一说其实塞默认值可能在序列化这一步更合适?
    字符串就 "",列表就 [],但是对象和数字不好处理,特别是需要区分‘填了’和‘没填’,需要有‘草稿’这类的功能的时候更是如此…
    j1132888093
        47
    j1132888093  
       2024-01-30 09:47:46 +08:00
    整型的 null 和 0 ,字符串的 null 和空本来就是不同的意义,凭啥不要 null ?你要是说 null 就直接不返回这个字段还能理解
    wangtian2020
        48
    wangtian2020  
       2024-01-30 09:47:54 +08:00
    返回的 json 出现 null 或者是 undefined 我立马活都不干了跟后端去 battle ,所有字段必须初始化成空字符串/空数字
    2123123
        49
    2123123  
       2024-01-30 10:40:38 +08:00
    字符串空跟 null 的绝大部分时候意义是一样的,所以字符串我一般初始值给 ""
    但是空数字意义是不一样的,非要传个特定数字或者 "" 代表空的话也要多一次判断,跟判断一次 null 本质上不是一样吗?
    leonshaw
        50
    leonshaw  
       2024-01-30 11:01:44 +08:00
    幼儿园就讲过的问题,0 不一定是没有,白色不等于透明
    jl1014171068
        51
    jl1014171068  
       2024-01-30 11:02:07 +08:00
    null 也还能接受,但是还有返回"null"的..........
    dyv9
        52
    dyv9  
       2024-01-30 12:49:24 +08:00 via Android
    对于数字栏,有时候没填写和 0 意思是不一样的。
    BBCCBB
        53
    BBCCBB  
       2024-01-30 13:11:20 +08:00
    OBJECT_MAPPER.setSerializationInclusion(JsonInclude.Include.NON_NULL);

    这样直接就不返回那个值了, 一样有问题.
    最好的办法就是前端判断..
    xingdaorong
        54
    xingdaorong  
       2024-01-30 15:44:29 +08:00
    @zjsxwc 你说的不合理,假如 age 字段不填,可以返回 null 不能 0,因为含义不同一个没有年纪,一个是 0 岁,字符串比如名字,可以是 null 可以是"",但是最好是"",因为前端可能用字符串方法不做判空报错。但是比如一个数组,没有数据就是空数组[]而不是 null 。总结:如果没有特殊含义就一定用同类型的,不用 null 。
    exmario
        55
    exmario  
       2024-01-30 16:11:57 +08:00   ❤️ 1
    null 还好说,但是给个"null"算什么回事
    zjsxwc
        56
    zjsxwc  
       2024-01-30 16:34:25 +08:00
    @xingdaorong
    我还是坚持除了 array 必须用空数组[]外,别的全都可以 null 。

    如果你不允许 null ,那么对于任意字段 XXX ,你必须额外添加一个字段 isXXXNotNull 来表示,XXX 字段是没有被填写状态还是已经被填写了。
    lujiaosama
        57
    lujiaosama  
       2024-01-30 16:46:24 +08:00
    我最讨厌的是数字型字段原本该返回 NULL 或者''. 给我返回了个-1. 问题是-1 有些场景在业务上是有意义的. 直接显示个-1 容易误导用户.
    wmui
        58
    wmui  
       2024-01-30 18:39:41 +08:00
    不是不能是 null ,是要保持数据结构一致。如果是数字,默认值可以是 0 不能是 null ,如果是字符串,默认值可以是'',如果是对象,默认值可以是 null
    zerofancy
        59
    zerofancy  
       2024-01-31 03:27:11 +08:00
    我们是基本类型一般不要有 null ,这样客户端可以定义 int 而不是 Integer 。其他类型包括 String 都当作可能为 null 来处理。
    尽管我们用了 Kotlin ,但序列化框架还是 gson ,所以一般还是把几乎所有字段定义成可空的。
    Jhon11
        60
    Jhon11  
       2024-01-31 07:28:57 +08:00 via iPhone
    用 protobuf
    yusheng88
        61
    yusheng88  
       2024-01-31 09:57:55 +08:00 via Android
    1. null 和默认值是两个概念,null 是没有值,判 null 是调用方的责任
    2. 字段能否为 null ,是产品,业务决定的,如果有默认值,也是产品和业务决定的。
    3. 字段为 null ,或者为 null 时不返回字段,前端处理不了的,领导还倾向前端的,只能对你表示可怜
    liuhuihao
        62
    liuhuihao  
       2024-01-31 10:48:34 +08:00
    个人观点

    String 空值情况:空值一律返回'',理由是一般情况下字符串字段都是用来展示用的,如有逻辑判断<用户是否填了>这个字段,那可以增加一个 boolean 字段表示是否填了, 但这种情况业务上很少,因为作为入参的时候一个输入框没填其实 = 空字符串

    Number 空值需要根据业务场景来处理,比如“好友数”等各种计数的空值可以是 0 ,男女等 未选择的话可以用 -1 表示。但如果是一个非必填数字输入框,可以输入任意值的这种,null 值 应该是需要的

    Boolean 同 Number ,需要根据业务场景来处理。

    Array 空值一律应为 []

    Object 不应返回 null ,比如“人”这个对象有个字段叫“父亲信息”,有的人可能没有父亲,这种情况 “父亲信息” 字段不应返回 null ,而是在“父亲信息”这个对象里面的 “是否存在父亲”字段做判断

    总结我的观点:除了 Number 和 Boolean 可以返回 null ,字符串、对象、数组都不应返回 null
    liuhuihao
        63
    liuhuihao  
       2024-01-31 10:58:18 +08:00
    @yusheng88 产品和业务决定的应该是 字段是否可以为空,帖子讨论的为空的时候接口是否该返回 null 。很多值为空时候返回其实是可以在接口设计阶段避免的。
    个人理解,如字符串 null 和'' 其实是一个含义都表示没填,数组[] 和 null 其实也是一个含义都表示没有。对象则应该在对象内部增加字段判断是否存在,Number 和 Boolean 大多数情况下也不需要返回 null 。
    QlanQ
        64
    QlanQ  
       2024-01-31 11:14:06 +08:00
    我比较赞同 #58 的观点
    接口的基本原则就是 数据结构必须一致,如果你的这个数据定义是 string 那这个值没有设置之前都是 '',不应该是 null
    上面有说 如果是 null 就不返回的情况,这种我见了就直接打,你都是强类型语言了,返回的结构还不一样?我在判断一个数据的值是不是真之前,还要考虑这个值存不存在?

    两个例子,现在已经有 1w 用户了,突然加了个需求,用户可以弄一个签名,类似 QQ 签名,这个时候,你要加表字段,按楼上一些人说的,那这个用户没有设置过,值应该是 null 是吧,那现在一个用户设了签名,后面又清空了签名,那你在保存用户的签名时候,是改成空字符还是 null ?你返回的数据是什么?

    上面提到年龄,如果这个字段的信息是保存用户的年龄,那 null 是什么意思呢?
    liuhuihao
        65
    liuhuihao  
       2024-01-31 11:37:58 +08:00
    数据定义和返回必须一致,这个我支持。你可以看下 我 62 楼的回复,有些情况下 null 还是有必要的。比如一个非必填输入框只支持填写任意的数字,那么这种情况下 我这边通常在定义这个字段的时候就定义为 number | null ,用 null 来表示“没有填”。
    xingdaorong
        66
    xingdaorong  
       2024-01-31 11:42:06 +08:00
    @zjsxwc 没听懂啥意思,这个是否被改写的状态真实业务场景的状态是啥,很多不需要这个字段
    yusheng88
        67
    yusheng88  
       2024-01-31 12:44:34 +08:00 via Android
    @liuhuihao
    1. 我的评论应该很清楚,接口该返回 null 就返回 null
    2. null 和"不是一个东西,等你什么时候调用接口后不用对 string 返回值判 null 再讨论这个吧。
    3. 集合,容器类推荐返回空集合,因为关注的是容器内的元素,而不是容器,容器应该存在的
    liuhuihao
        68
    liuhuihao  
       2024-01-31 15:05:41 +08:00
    @yusheng88 #67 没理解你的意思。我们这边接口针对 string 、数组、对象 类型前端确实是不需要 判 null 的,因为约定的是啥类型返回就是啥类型,空值也一样,string 类型为空时当然应该为空字符串,null 又不是字符串类型,类型定义和数据返回的类型应该一致。
    jones2000
        69
    jones2000  
       2024-01-31 15:15:39 +08:00
    null 字段,直接不要返回就不可以了, 还能节省流量。
    yusheng88
        70
    yusheng88  
       2024-01-31 15:23:59 +08:00 via Android
    @liuhuihao
    话不投机半句多
    你保持你的接口返回值没有 null 就行了。
    不用再回复,没兴趣解答。
    lancelock
        71
    lancelock  
       2024-01-31 17:56:43 +08:00
    空数字是各什么概念,哪个数字是空的?
    Coder89757
        72
    Coder89757  
       2024-01-31 18:11:14 +08:00
    @doco #1 主要原因是给了 null 之后,解构取值兜底逻辑会直接失效。。。

    ```javascript
    const { str = '' } = response;
    // 假如 str 直接不存在,对象中连这个属性 key 都没有,前端拿到的 str 就是 undefined ,此时会兜底成空字符串,下面这个语句会正常运作,UI 正常渲染
    const newStr = str.replace('foo', 'bar');
    // 假如 str 传了 null ,这里直接 error 了,假如这段逻辑在 React 或者 Vue 渲染逻辑里面,这块 UI 直接就白了
    ```

    核心问题还是在于,这种做法改变了返回字段的类型,把前端或者客户端,抛进了类型判断的屎山地狱
    DzwsGo
        73
    DzwsGo  
       2024-02-01 14:54:03 +08:00
    @chendy
    #46
    说的很对,但是答非所问,自己的逻辑 null 很好处理,但是接口里如果冷不丁返回 null 的话,做处理就会所有的参数都加上?,不做处理转对象的时候就会崩。
    #9
    这跟 ui 框架没有关系
    关于   ·   帮助文档   ·   博客   ·   API   ·   FAQ   ·   实用小工具   ·   5315 人在线   最高记录 6679   ·     Select Language
    创意工作者们的社区
    World is powered by solitude
    VERSION: 3.9.8.5 · 28ms · UTC 07:34 · PVG 15:34 · LAX 23:34 · JFK 02:34
    Developed with CodeLauncher
    ♥ Do have faith in what you're doing.