前后端分离:谁来主导接口和数据格式定义?

发表时间: 2019-04-18 04:07

都是说需求其他的任何人都不管,交给程序员的只是需求,两次前后端分离,深深感觉到写个页面两天搞定,联调花了五天,我知道只是一开始前后端没有协商好接口,以及数据格式造成的,所以,我觉得一开始就应该协商接口以及数据格式,但是转念一想,还没开始开发呢?谁知道中间有哪些坑?所以这种协商给我的感觉是没有意义。

人物1:

没人主导 肯定架构说的算,没有架构,就是后端出服务,前端管内容格式,程序员思维都是点,最好把争论放下,多想想交互设计.先定服务架构,再定接口,一定要评审,没人是主导,业务与架构是主导

人物2:

我们一般后端定接口,定好了之后前端会根据交互完成审核下接口,双方都敲定后就开始开发。初期后端自己玩,前端也自己玩,双方没有必要关心对方在做什么,只按照接口办事即可。

作为前端,为了提高开发效率,我也做了个 mock 相关轮子,有兴趣可以看看这个:一个简单的 mock 工具应该长什么样(里面详细讲了从前端视角出发整个开发流程), 这个我们组都在用,效率确实提高不少

人物3:

理论上是,谁更有经验和能力谁写,让前后端都方便,然后大家讨论。

实际是,都是后台定。然后写代码的过程中遇到问题后台再按自己的想法改。

规范能够大量减少沟通成本,另外笔者认为它还有一个隐性的作用,如果有了规范,在写代码的时候可以省略很多思考和修改,从而增加开发效率。所以笔者一直对规范这块有着一种执念,总会在规范方面想很多。比如最近就在思考API的一些规范,参考RESTful API的思想,微创新出了一套过渡性的类REST规范,可以在仅用GET、POST的情况下,形成一定的规范。

下面用一个 公司列表 -> 公司详情 -> 部门列表 的场景来举例对比

公司-查GET /corpsGET /corpsGET /corps

公司-增POST /corpsPOST /corpsPOST /corp/create

公司-删DELETE /corps/:corpIDPOST /corps/:corpID/deletePOST /corp/delete

公司-改PUT /corps/:corpIDPOST /corps/:corpID/putPOST /corp/modify

公司-局部改PATCH /corps/:corpIDPOST /corps/:corpID/patchPOST /corp/update

公司详情-查GET /corps/:corpIDGET /corps/:corpIDGET /corp

部门-查GET /corps/:corpID/departsGET /corps/:corpID/departsGET /depart/list

部门-增POST
/corps/:corpID/departsPOST
/corps/:corpID/departsPOST /depart/add

部门-删DELETE
/corps/:corpID/departs/:departIDPOST
/corps/:corpID/departs/:departID/deletePOST /depart/remove

部门-改PUT
/corps/:corpID/departs/:departIDPOST
/corps/:corpID/departs/:departID/putPOST /depart/save

部门-局部改PATCH
/corps/:corpID/departs/:departIDPOST
/corps/:corpID/departs/:departID/patchPOST /depart/change

了解目标

  1. 尽可能的缩小沟通的成本,开最少的会,确定大部分的事。
  2. 花最少的时间写文档,保证90%的开发人员看懂所有内容。
  3. 哪怕不看文档,也能知道各种接口逻辑。
  4. 不重复写代码
  5. 尽可能的写高可读性的代码

我们做了哪些事

  1. 完成了前后端分离
  2. Android、ios、web共用一套接口
  3. 统一接口规范(post、put、get、patch、delete)
  4. 统一了调试工具
  5. 统一了接口文档

之前的接口是这样子的:

接口地址含义请求方式

…/A项目/模块1/getProducts获得产品GET

…/A项目/模块1/addProduct添加产品POST

…/A项目/模块1/getProductDetail获得产品详情GET

…/A项目/模块1/editProduct修改产品POST

客户端请求是这样的:

…/A项目/模块1/getProducts?id=1&a=2&b=3&c=4&d=5…………

A页面=====》B页面(携带n个变量)====》C页面(携带m个变量,包含i个A页面的变量) ——-经常n>4

大部分请求是POST,至于put、patch、delete是什么鬼,关我屁事。

关于接口入参使用json,那完全是看开发心情。

标准参数是:

{“message”:”success”,”code”:0,”data”:具体内容} 

其中data里包含数组可能是

[{“a”:”1”,”b”:”1”},{“a”:”1”,”b”:”1”},{“a”:”1”,”b”:”1”},{“a”:”1”,”b”:”1”}] 

即使下一个页面用到也不会使用id,而是把所有字段都传进去。

A接口中,返回产品用product;B接口中使用good,多个接口很可能不统一。

客户端对接是这样子的:

  • 安卓、ios一套;部分接口各自用一套;html5端一套。
  • 客户端和后台是不停交流的

接口文档是这样的

  1. swagger
  2. 阿里的rap
  3. Word文档
  4. 其它

觉得swagger和rap神器都是非常强大的,能够实现各种功能逻辑,但是考虑到开发人员掌握程度不通,复杂度较高,难以提高效率,我决定初期并不使用这两样神器。

传参约定

约定token身份认证统一传入参数模式,后端采用aop切面编程识别用户身份。其他参数一律为json。

resultfull接口约定

首先我们选择一个名词复数,比如产品

post方法

新增一条XXX

比如 ……/products 则代表新增一条产品

传参json如下:

{ "name":"我是一款新产品", "price":100, "kind":"我的分类", "pic":[一组图片], 等等还有很多}

put方法

新增某条XXX记录

比如 ……/products/1111111111

传参json如下:

{ "name":"我是一款新产品", "price":100, "kind":"我的分类", "pic":[一组图片], 等等还有很多}

get方法

获得所有XXX

……/products 则代表获取所有产品

因为有分页,所以我们后面加了?page=1&pageSize=50

我们约定了所有名词复数,都会返回list,且list每个对象都有字段为id的唯一id。

比如

{ "data":{"list":[{"id":"唯一id","其他很多字段":""},{"id":"唯一id","其他很多字段":""}],"page":1,其他字段}, "code":0, "message":"成功"}

……/products/{id} 获取某个具体产品(一定比列表更详细)

比如某个具体产品里还包含一个list,如该产品推荐列表,则:

……/products/{id}/recommendations

假设它包含的不是一个list,而是对象,比如产品佣金信息,则:

……/products/{id}/Commission

patch 方法

更新局部XXX产品YYY信息

入参是post方法时入参的子集,所有支持更新的参数会说明,并不是支持所有变量

……/products/{id}

{ "name":"我是一款新产品", "price":100, 部分变量}

delete方法

删除XXX记录

……/products/11111

删除11111产品。

其他说明

我们尽可能少的使用动词,但有一些行为需要使用动词,比如登录等。

关于版本号,我们打算在模块后增加/v1/等标识。

权限约定

服务端要对用户角色进行判断,是否有权限执行某个逻辑。

前后端分离约定

后端以开发接口为主,不再参与页面开发,或者混合式jsp页面开发,统一以接口形式返回,前端通过js渲染数据以及处理逻辑。

共用接口

web、Android、ios使用统一接口,不在因为哪方特殊要求额外开放接口。

使用postman最为接口文档、调试工具

虽然有上文中介绍的rap和swagger都是特别牛的接口神器,但是我们还是选择了postman,开发人员将接口名称、说明、入参、出参,以及各种出参示例都存储,这样开发直接可以看得清接口含义。