可能是另一场圣战:后端返回的 JSON 的值是只要 String 类型呢,还是各种类型都包括呢?

2018-05-19 15:21:21 +08:00
 winiex

工作中和不同的客户端开发者合作过,有的要求返回的 JSON 统一只包含 String 类型:

{ a: "THYM", b: "107", c: "false" }

而有的则要求数据要表达自己的类型:

{ a: "THYM", b: 107, c: false }

我个人是支持第二种写法的,因为不用再写一堆转 String 和转回类型的代码。大家团队都选择何种方式呢?又是出于何种原因与理由呢?

19183 次点击
所在节点    程序员
161 条回复
huclengyue
2018-05-19 23:35:51 +08:00
@janxin 以前 oc 时代就这样,我是做安卓的我自己当然是喜欢第二种了,因为 java 各种解析库很完美了,但是有时候遇到 php nodes 等这种弱类型的后端,穿个 int 过来,可能其实是 long。。所以为了稳定减少 bug 还是 string 好一些
FrailLove
2018-05-19 23:36:43 +08:00
各种语言都有现成的库 轮子也没要你造 调调库都嫌麻烦了
huclengyue
2018-05-19 23:37:12 +08:00
@missdeer 并不是这个原因,如果是懒汉更喜欢第二种吧,有时候弱类型语言对接强类型语言,第二种很容易出问题。
luoyou1014
2018-05-19 23:38:55 +08:00
@HangoX
@incompatible

这类问题不可能完全解决,总归会有漏掉的地方,如果是一次性开发完就不在更新的产品,自然是把所有出问题的地方都解决了即可。

但是在大型的不断集成新功能的产品,app 有了十几个版本,每个版本的接口都会一直运行,老版本的接口在当时的需求设计下是没有问题的,但是后续需求变动,导致老版的接口出现异常,然后 app 就会出现闪退的情况。还有团队里面会有快速的需求迭代,以及临时的 bug 修复提交代码到线上,这些都有可能造成异常问题。

代码 review 解决的还是一个有序的问题,但实际情况下不会有时间给每个接口做足够的检查,有 review 的时间还不如让开发自己写接口测试。大型项目里面还存在大量接口转发的问题,例如我的功能里面部分页面已经有其他人现成的接口写好了,我不过是调用对方生成好的数据返回给客户端,这种情况下,天知道对方的代码有没有类型错误的问题,最简单的方案就是直接全部转成 string 来解决。

最终大家偏向全部 string 方案的原因还是类型错误的这个问题太低级了,但是又很难杜绝,但是出现的闪退问题却又是测试里面被认定为最严重类型的问题,多搞几次这类错误,团队里面谁都扛不住压力,大家只能放弃理论最优方案,转而使用比较 dirty 的方案。
huclengyue
2018-05-19 23:40:18 +08:00
@james2013 还有就目前遇到的问题,php 的 int,有时候对应的其实是 java 的 long,用 gsonformat 解析出来是 int 虽然不会报错,但是数据其实是错的。
wintercoder
2018-05-19 23:40:38 +08:00
你们的重点是不是歪了?怎么都讨论 false 和 0 的问题了,0 和 1 替代 true/false 应该没什么人反对吧。说回类型吧,107 和"107" 其实我是懒得处理的,数据库查出来是啥就是啥
sagaxu
2018-05-19 23:41:38 +08:00
@huclengyue 我司 app ios 和安卓都有,后端基本都是 php,新来的员工才会犯这种返回值类型错误,而且这类 bug 都消灭在联调阶段了,连测试阶段都到不了。
huclengyue
2018-05-19 23:43:40 +08:00
@sagaxu 这个问题是可以解决,但是直接传 string 能直接避免这个问题
cloverstd
2018-05-19 23:48:58 +08:00
你们不知道 JS 里面最大只能是 i53 吗
rbe
2018-05-19 23:52:12 +08:00
目前团队约定在用的就是类似第一种,不过很多问题的处理不像上面的某些说法理解的那样。

1. 对 Object,不是直接序列化成为一个字符串,而是继续保留嵌套层级结构,只是处理 数字、bool 类型;
2. 对于 null 值,必传字段加默认值,非必传字段不传;
3. 对于 bool 值,很多时候很多字段你是并不能那么确定他一定是非真既假的,举例如你要做一个是否收货的列表,最开始需求不明确快糙猛写成了 `已收货 / 待收货`,你用 bool 值 `isDelivered` 来表示。后来要你加 `全部 / 待付款 / 待评价` 这些状态岂不是要懵逼,所以用 0, 1, 表示是比较保险的;
4. 对于数字型,就是前面有人提到的,假设后端是动态语言,谁也不能保证代码多了以后,返给前端的接口一定是 int / float / double 区分开的。返回一个 1.0, 客户端写的是 equals(int 1), 就会闪退

这是我和我们客户端沟通的一些结果,一切都是为了降低客户端的闪退率,毕竟他们的代码发布出去了就没法调整了,而有时候你很难去避免一些错误。

有什么不对或者说的不好的地方,欢迎继续讨论
flyingghost
2018-05-20 00:07:29 +08:00
1,不谈 json,绝大部分持久化协议都有类型系统。为什么?可能这些协议设计者规范制定者都是傻子吧!
2,json 大数字导致 js 解析失败的问题,是怪 json ?我扔个 20 位的字符串你 js 真的就能按 long 处理了吗?我扔个 2000 位的呢?我扔个 10^20 的大数字字符串把你内存撑爆了你是不是还是怪 json ?冤有头债有主,https://www.npmjs.com/package/long 了解一下。要搞科学计算的,请自己实现大数字的解析和计算。别死抓着系统 json 解析库不放,却怪字段类型。
3,类型错误导致异常的同学,硬生生通过消灭错误的根源解决了错误。你们真厉害,跟 zf 一个处理思路。那值错误问题呢?想个办法也消灭掉?参数个数问题呢?变长参数嘛!参数顺序问题呢?调用时用字典传参写明 key/value 对嘛。参数名拼写问题呢?我编不下去了。。。不过我想聪明人应该能解决,办法总比问题多嘛!

——以上是恶搞,以下是正经分析——

其实还有一点是非常重要的:信息抹除。原接口设计和数据 /参数持久化是携带了类型信息的,被硬生生抹除了。
接口 a 需要一个 int,传了个"0",
接口 b 需要一个 string,传了个"0",
接口 c 需要一个 bool,传了个"0"。
他们默默做类型转换的时候,揣摩通透服务端的意图了吗?万一服务端真的 bug 了,就输出了一个错误的类型,你能察觉吗?
这是信息抹除带来的第一个问题:错误被掩盖。
第二个问题可能是某些人习以为常的:文档上说入参均为 string,但其实具体实现中会对他们做对应的转型操作。转错了程序当然没法正确执行。这使得人们更依赖所谓的文档,却直接丢弃了语言 /协议 /架构 /方案自身的自描述和自检能力。“最好的文档就是代码本身”这句话的价值不是为懒得写文档找借口,而是真正的金玉良言。
第三个问题随着第二个问题而来,文档缺失和过期大概是实际项目中的常态,信息抹除所带来的额外信息恢复工作就变成了一项困难的事,增加更多不必要的成本和风险,以及团队沟通成本、技术管理成本。

那么为什么很多“给别人用的接口”反而倾向于这样设计呢?容错性表面上能稍微好一点,而额外带来的风险和成本又不必自己承担。典型的就是给第三方用的接口、给其他部门用的接口、给其他团队用的接口。后端给 app 扔个这样的接口,不必沟通,不必争论,暂时性的增加了双方的幸福感。有种让他在团队内部同一项目内声明一堆 Object... args 的方法?怕不被自己人打死。

说白了,这就是懒惰+短视+自私的人性抉择。
hyyou2010
2018-05-20 00:10:29 +08:00
@james2013

关于{desc:"null"},如果按我的想法,这里应该是{desc:""},从未设置过也是“”,也即只有文本字符串的概念。

有很多 json 解析工具的确都可以处理 js 中的 null,然而不一定所有地方都用同一种解析库,也说不定不叫 null 而叫 nil,因而在项目中往往更看重接口的普通性和统一性,容错处理也是这样,这样才能避免坑。印象中---但我可能记错---接口定位为 boolean+int 和 Boolean+Integer 可能在解析包里面的处理就不一样,而这挺容易写混。诸如此类吧,我认为接口应该就平面无对象的,和语言无关,和解析库无关。
huclengyue
2018-05-20 00:39:16 +08:00
@flyingghost。。。。不敢苟同,相对的我认为懒人才更会选第二种,因为 json 解析库自带的功能。在对接不同语言,不同平台,不断试错之后选择的第一种并不是懒,或者自私。难道你们的语言里对象转 json 比转成第一种这样的方式更容易?
james2013
2018-05-20 00:40:38 +08:00
@huclengyue
1.ios 中没有 true 或者 false,这个是很多替代方法:
1)比如 ios 本地处理(不会 IOS,这这只是从网上搜索的,结果待验证,我想是有解决办法) "manage":true BOOL manage = [[content objectForKey:@"manage"] boolValue];
2)实在不行,规定以 1,0 代替 或者字符串值来代替 true 或 false
2.json 中我才不管你是 php 还是 java 后台,我公司后台 java/php/.net 都有,人家也不会告诉是什么类型,我也是根据接口返回值自己进行修改,觉得用户 id,订单 id 等需要 long 类型,就修改了.以前还真遇到过 GsonFormat 自动生成的是 int,但是实际上是 long 类型的.
huclengyue
2018-05-20 00:44:24 +08:00
@james2013 额。。。所以我们说的不冲突啊。。。
flyingghost
2018-05-20 01:03:47 +08:00
@huclengyue 战术层面的勤劳掩盖了战略层面的懒惰。任何语言里额外转一次 string 都是体力活,但好在不用动脑呀。
相反,选择自带库不是懒惰,这是聪明的偷懒,因为脑力都耗费在设计上,耗费在方案选择上了。就算自带库不能满足需求(比如 js 自带 json 解析不能满足 long 需求)依然花力气去找轮子而不是造轮子。有现成的谁不爱用呢是吧。
james2013
2018-05-20 01:04:28 +08:00
@hyyou2010
你说的接口跟语言无关,我是认同的.json 格式,是有其规范的.
解析库也是依据 json 格式进行封装的,什么?连 json 格式中 null 都不支持的解析库,找一个正规的出来看看 ?. 像你说 nil 这种情况是不会出现的.
json 的值就只有 string,number,true,false,null,object,array.这几种类型.如果只用 string,就要想想为什么 json 还要制定的其它类型
以下是 wiki json 例子:https://en.wikipedia.org/wiki/JSON
{
"firstName": "John",
"lastName": "Smith",
"isAlive": true,
"age": 27,
"address": {
"streetAddress": "21 2nd Street",
"city": "New York",
"state": "NY",
"postalCode": "10021-3100"
},
"phoneNumbers": [
{
"type": "home",
"number": "212 555-1234"
},
{
"type": "office",
"number": "646 555-4567"
},
{
"type": "mobile",
"number": "123 456-7890"
}
],
"children": [],
"spouse": null
}
waitwait365
2018-05-20 01:30:07 +08:00
第一种很 low,第二种合理。
但是其实第一种的最根本的想法是解决一些问题的妥协的土方法。尤其是不同团队或者不同公司间的对接项目。(比如:毕竟我接口就一个入口,改起来快,调用方 n 个地方调用这个接口,经常会出现改漏了测出来有问题再改再测,虽然本质是技术能力问题,但没辙,延期半天,谁想周末来加班,算了算了,我 /你 做下兼容。本质就是替别人买单或者别人替我买单)
waitwait365
2018-05-20 01:38:41 +08:00
还有一个比较有意思的事情:几年前了,我们接淘宝订单,有天 A 团队.net 的,订单服务有问题,后来查下来原来是淘宝一个字段的数据类型改了,B 团队订单服务却没问题,后来看了下原来处理订单的时候可能默认所有字段都是 string,计算的时候都强制转了下类型。A 当然没错,B 做法相当笨拙,但是结果是 A 被客户抱怨(后来没关注淘宝是不是又改回来了)
hyyou2010
2018-05-20 01:39:25 +08:00
@james2013
解析库也是依据 json 格式进行封装的,什么?连 json 格式中 null 都不支持的解析库,找一个正规的出来看看 ?. 像你说 nil 这种情况是不会出现的.

---------这里有个误会,我是说后台有可能送的不是 null,而是 nil 或其他。

我的观点是,接口规范和 json 规范是两码事,接口设计不必遵循 json 规范,它们有不同的设计标准。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/456138

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX