前端全栈解决方案Javascript

前后端接口之争的解决原则

2020-08-19  本文已影响0人  microkof

前言

前后端在接口上永远在争斗,各种论坛总少不了这种吐槽,但总要有什么办法去解决这个问题。

首先保持积极的平和的心态最重要,提高个人水平也迫在眉睫,在这个前提下,依然会存在一些争端,这些争端分两种:

这两种我都说一下,我希望用下面这些原则来解决:

原则一、坚定的遵守团队制定的API规范

说话算数,这是正常人起码的行为准则。前后端之前约定好了RESTful API的规范,但是后端不严格执行,说好新增和修改是2个接口,结果有时候就合并成了一个接口,有时候用DELETE方法,有时候就成了POST方法,你永远不知道对方什么套路,这绝对不行。

原则二、不要给别人找麻烦

例1,后端写完接口不测试

个别后端认为:反正前端跟我联调的时候会测试,我Postman跑一次能跑通就得了。

实践证明,个别后端所谓的“能跑通”,是在理想化参数、理想化权限、理想化数据内容的前提下能跑通,甚至在修改了一些代码之后并没有做再次测试。

一个团队往往后端的人数是前端的2倍以上,而且倾向于压缩前端人数,这个前提下,不要给前端找麻烦。

例2,后端对响应体过度包裹

比如前端要的结构是:

{
    "msg": "操作成功",
    "code": 200,
    "data": {
        "a": [1,2,3,4,5,6],
        "b": "haha"
    }
}

某后端给的结构是(情节有所夸张,但也有代表性):

{
    "msg": "操作成功",
    "code": 200,
    "data": [
        {
            "resultVo": {
                "a": [
                    {"value": 1},
                    {"value": 2},
                    {"value": 3},
                    {"value": 4},
                    {"value": 5},
                    {"value": 6}
                ],
                "b": "haha"
            }
        }
    ]
}

简直就是屎山!各种无用的嵌套!最牛逼的是那一坨value是什么鬼?!

还有个resultVo,前端问:Vo是什么?后端说:“你不用管”。。

那我不管,你来写前端,好么?

不要给别人找麻烦!

例3,响应体加了莫名其妙的Java术语

专门说一下这个可笑的问题吧,比如:

{
    "msg": "操作成功",
    "code": 200,
    "data": {
        "userListPojo": []
    }
}

请问ListPojo是什么Pojo?这群user都姓Pojo?

不要给别人找麻烦!

例4,后端自己能取到的数据,却找前端要

WTF!!我传了别人的userId,而你根本不打算做验证(取都懒得取,当然不会做验证),那我是不是可以攻击别人的账号了?

大哥,当前用户的部门ID是可以数据库自查的,就像你能查当前用户的姓名性别出生年月一样,让前端给你传部门ID做什么??如果我传了一个虚假的部门ID,你做不做验证?不做验证的话,我做越权攻击你怎么防?

不要给别人找麻烦!

例5,前端传无用字段,后端返无用字段

后端需要10个字段,前端给了18个,有8个无用字段,却美其名曰“用不到可以无视”,神他妈的“无视”!结果后端DEBUG的时候眼花缭乱。

同样的,前端需要10个字段,前端也给了18个,也美其名曰“用不到可以无视”,结果前端调试也眼花缭乱。

最后,用户说:“老子流量不要钱的?浪费老子流量!”

如果程序员连过滤自己发出的字段都做不到,请辞职。

不要给别人找麻烦!

例6,后端要求传重复的字段

比如,某后端要求前端传:

"userId": 111,
"personId": 111,

前端问:这俩字段有什么区别?后端说:没区别,因为要存到两个表,所以传2遍。前端问:那么这两个字段的值永远是一样的?后端说:是的。前端问:那我也得传2个字段是吗?后端说:是的。

是你大爷,你怎么不去死呢?

不要给别人找麻烦!

例7,字段语意不明

一个接口,后端返回:

"nickName": "张三",
"realName": "李四"

某后端说:nickName是顾客名字,realName是服务员名字。

我特么砍死你信不信?

不要给别人找麻烦!

例8,字段名不统一

有一个接口,某后端返回:

"roomId": 111

他说这是房间ID。

OK。

后来又有一个强相关的接口,同样的数据,某后端返回:

"houseId": 111

他说这是房间ID。

一会room一会house,砍死你信不信?

他说好吧,改一改。然后改成了:

"room_id": 111

特么驼峰又去哪了?咋下划线了?

不要给别人找麻烦!

原则三、二次加工数据由前端加工还是由后端加工?视情况而定

例1,递归加工数据

比如,我希望数据库保存某人在公司的所属部门,比如他属于:

某公司->销售大部->市场部->市场1部->华北片区组

假如华北片区组的部门ID是1324,前面几级的ID分别是1, 4, 118, 567,那么,他的部门应该在服务器应该保存为1324?还是保存为1, 4, 118, 567, 1324呢?显然应该保存为1324,因为部门树状结构是可能会变的,把1, 4, 118, 567, 1324写死到数据库是绝对不行的。

那么问题来了,前端要显示面包屑导航,也就是显示成某公司->销售大部->市场部->市场1部->华北片区组这样子,这个字符串由前端负责还是后端负责呢?视情况而定。

  1. 如果后端给出了一个全公司部门的树状JSON,那么,面包屑应该由前端负责,因为由服务器做递归计算是很不划算的,收益小成本高,而由前端递归计算面包屑,收益高成本低,毕竟本地JS运算不消耗服务器资源。
  2. 如果后端给不出全公司部门的树状JSON,那么,面包屑只能是由后端负责迭代计算。

例2,传数组还是分别传?

比如一个时间选择器,有开始时间和结束时间,这种组件通常会生成数组:

['2020-02-02','2020-12-12']

那么前端应该给后端传数组还是拆分成2个字段呢?

我认为这个应尊重后端的意见,因为这2个字符串通常在数据库存储为2个字段,所以后端通常会要求前端按2个字段来传,前端没必要因为此事争执,我建议前端先拆分为2个字段,然后再传,除非后端说,前端随意传,后端都能处理。

那么数据库可否将2020-02-02,2020-12-12存入一个字段里呢?虽然也可以,但是修改其中一个值就比较麻烦,需要先取出再存入,计算的时候也需要先打散成数组再去计算。服务器运算收益低。

那么数据库这两个字段的值,返回前端,是按数组传还是分别传呢?

依然应尊重后端的意见,如果后端说分开返,那么就分开返。况且,如果前端有一天因为某某原因不再用Date-Range组件,而是分成2个独立输入框,那么返回数组反而麻烦了。

例3,时间数据怎么传?怎么返?

时间一般说有3种格式:

  1. 标准dateString,例如:Thu Aug 20 2020 10:08:20 GMT+0800 (中国标准时间)

  2. 高可读dateString,例如:2020-08-11 00:00:00

  3. 毫秒级时间戳,例如:1597889706250

怎么传,怎么返,也应尊重后端要求,因为无论哪一种时间格式,前端都能用转换器轻松转换。

转换 方法
标准dateString -> 高可读dateString 需写工具函数,例如new Date('Thu Aug 20 2020 10:08:20 GMT+0800 (中国标准时间)').getFullYear() + ...,或工具库处理
毫秒级时间戳 -> 高可读dateString 需写工具函数,例如:new Date(1597889706250).getFullYear() + ...,或开源工具库处理
标准dateString -> 毫秒级时间戳 new Date('Thu Aug 20 2020 10:08:20 GMT+0800 (中国标准时间)').getTime()
高可读dateString -> 毫秒级时间戳 new Date('2020-08-13 11:11'.replace(/-/g,'/')).getTime() 或工具库处理
高可读dateString -> 标准dateString new Date('2020-08-13 11:11'.replace(/-/g,'/'))或工具库处理
毫秒级时间戳 -> 标准dateString new Date(1597889706250)

注意:高可读格式转其他格式时,某些浏览器的new Date()对格式的要求会比较苛刻,比如不允许短横线、只允许斜线分隔等等。因此,要么上传2020-12-22,由后端处理,要么由.replace(/-/g,'/')替换再转换,要么由专业工具库处理成标准dateString或毫秒级时间戳。

例4

比如,前端为了适应前端框架、为了业务,需要把一个一维数组变成二维数组,那么请前端自己完成变换计算,不要让后端计算好了再传过来,除非后端返的数据不够,不足以让前端完成变换计算,或者前端必须递归推测到底需要get多少组数据,这种情况为了严谨性,最好由后端计算。

又比如,前端想给数组内的对象增加属性,比如增加个isEditing: false(正在编辑)属性,这种属性不能让后端返回,应该前端自己遍历加上属性。

原则四、优先照顾封装

前端打算封装一个《性别下拉菜单》组件,要求后端提供一个性别字典接口,后端利索的提供了,见下。那么之后,整个项目必须用sex表示性别,不要时不时来一个gender字段,这样不仅仅造成混乱,而且封装组件就很繁复,随时要防备你来个新名词。虽然A页面永远用sex,B页面永远用gender,分别处理,并不会引起bug,但是这绝对会增加维护难度。组件必须又能接收sex,又能接收gender,烦不烦?!

同样的,请永远尊重字典接口里的dictValue,既然规定好了0永远代表男性,1就永远代表女性,那么,就别自作主张又增加一个字典表,去让1代表男性,2代表女性。虽然只要2个字典表坚持不再变,确实不会引起bug,但是会增加维护难度。

{
    "sex": [
        {
            "dictLabel": "男",
            "dictValue": "0"
        },
        {
            "dictLabel": "女",
            "dictValue": "1"
        }
    ]
}

还有,如果定了"dictValue"值为字符串,那么业务接口返回数据就不要给数字型。后端自己跟自己的接口都矛盾,还让前端去擦屁股?

不要给别人找麻烦!

原则五、写代码前,先定名词字典

这里说的“名词字典”不是上面说的“数据字典”,比如:

房间,有人叫house,有人叫room

充值,有人叫recharge,有的人叫invest

小区,有人叫estate,有的人叫residentialarea

姓名,有人叫nickName,有的叫nickname,有的叫realName,有的叫realname,有人叫fullName,有人叫fullname,还特么有人叫name

凡是不确定的字段,别自己自作主张,团队商量一下,再落笔不迟。

一旦确定一个译法,就项目内永远用这一种译法!

上一篇 下一篇

猜你喜欢

热点阅读