前段时间接受了公司内的一个老项目,由于项目之前的开发工期很紧张,导致项目整体质量不高。 因此计划重构一下老项目,重构过程中遇到许多原先接口设计上的不合理导致的坑,因此决定写点什么,聊一聊接口设计规范和接口文档的维护。
分为两部分来讨论 1. 接口设计 2. 接口维护 3. 接口与开发相结合
一. 接口设计
“接口”顾名思义,是前后端用来交互的桥梁,在设计的过程中既要考虑到前端也要考虑到后端,因此开发的过程中一定是 “文档先行,开发后置” 的原则。
1. 谁来制定文档?
接口文档应该由后端编写还是由前端编写这个问题,不同的公司有不同的处理方案。
个人建议 由前端设计接口路由与结构,后端设计字段名称 。在设计的过程中,前后端需充分讨论评估。 因为接口的路由和结构与前端交互息息相关,如果完全交由后端来设计,可能会出现同一业务多次调用接口的问题,影响用户体验。而接口字段则是与数据库字段息息相关,而前端则不太需要关心,因此交给后端设计较为合理。
当然,不同的场景适用不同的方式,还是要具体问题具体分析。比如开发的产品是一个偏服务类型的产品,那么将接口设计交给后端开发则更为合理。
2. 是否要用 RESTful 风格?
个人感觉 RESTful 风格 较为适合仅包含 “增删改查” 等基本业务的小项目,复杂项目不建议,原因很多:
- RESTful 将参数分散到 请求方式、路由本身、路由参数、请求参数四个地方,不方便
- 请求方式
post get delete patch put
动词过少 - 状态码和实际业务差距大 等等
3. 接口请求方式、路径及参数
- 推荐 RPC式 ,即
- 请求方式 一律 POST
- 路径均为 /命名空间/资源类型/动作 ,如
/common/userInfo/get
和/common/userInfo/add
。如果项目内部接口均为查询类型接口,如后管报表项目动作均为查询,则动作部分可以隐藏掉,视情况而定。 - 参数 全部位于请求body里
- 接口遵循 单一性原则 ,每个接口只应服务于单一的业务,如登录接口只应负责处理登录业务,相关的用户信息应单独有一个 获取用户信息的接口。好处就是某些情况下可以自由刷新用户信息。如两个列表的业务功能与字段结构均相同,则可以合并为一个,反之则不行。
- 可扩展性与高可用性 设计接口时应考虑到多种情况,如将来的模块化收藏功能或者后端同一套 Sql 的问题
4. 接口返回数据
数据格式以 JSON 作为形式,即返回格式为 application/json
, JSON 的字段是以 { key: value }
的形式成对出现的。
对于 key 的规范
-
语义化命名
语义化即使用有意义的名称,如用户名使用userName
而不是data1
-
格式统一
如使用驼峰格式,则整个项目均使用驼峰格式,不要混用不同的格式 - 多个结果复数
如字段 value 为普通数组,则使用其复数作为命名。
如字段 value 为对象数组,则在其后面添加List
{ "tabs": ["按梯队", "按年级", "按教龄"], "schoolList": [{ "id": "001", "name": "北京学校" },{ "id": "002", "name": "上海学校" }] }
- 布尔返回值则配合动词进行命名
如果返回值是 布尔类型,往往在命名的时候可以添加动词{ "isTeacher": true, "hasChild": false, "canExport": false }
- 合理使用缩写或省略
缩写可以提高开发时的书写效率,也可以避免出现过长的字段命名。 但是使用缩写也要 遵循缩写规范,或者仅使用普遍认知的缩写方式,不要自己创造一些只有自己看得懂的缩写方式, 如果不知道该怎么缩写,那么干脆就不要缩写。level --> lv message --> msg error --> err userId --> uid
省略是指业务含义过长时, 省略其中不重要的部分翻译,只保留核心部分 。 如 “结转收入列表”接口下,仅包含“同期结转收入” “当期结转收入”这两个字段,那么“结转收入”实际上就是可以被省略的部分,保留“同期”和“当期”命名为
same 和 current
省略时主意 不要省略掉重要区分字段的部分,否则可能会导致命名重复。 - 不要使用重复 Key
Json 同级不应出现相同的 key,后面的会覆盖掉前面的
Json 不同级不建议出现相同的 key,虽然允许但书写时容易出现问题。如res.data.data.data.name
对于 value 的规范
-
使用正确的类型
-
String: 用于纯文本。
不要所有类型都用 String 的形式返回。
返回类型不确定,如既有可能是数字也有可能是文字时,统一使用 String 万能类型 返回类型即使为数字,但不参与前端数学运算仅供展示的时候,可以考虑使用 String 类型 返回 类别 时,可以使用 String 类型的 字典标识。 -
Number: 用于数值类型。 当返回的类型为需要前端进行计算判断的数字时,使用 Number 类型
-
Boolean:用于判断条件
当返回的值明确为 二元 判断时,使用 Boolean。如“是否完成”,如果仅包含完成和未完成两种状态则应为 Boolean 类型,如包含第三个“不确定”状态,则应更名为“完成状态”,类型也应为 String 字典类型 -
Array: 用于列表类型
数组内元素类型应一致,且用于同一个业务含义 -
Object:用于对象类型
-
-
使用合适的空置处理方式
- String 空值应为’’
- Number 空值需要根据业务场景来处理,比如“好友数”的空值可以是0,但有些情况需要区分不存在和0的情况,则需要返回 null
- Boolean 同 Number ,需要根据业务场景来处理。
- Array 空值应为 []
- Object 空值应为 {}
- 包含嵌套的对象或数组,如遇到空值需要具体情况具体分析
其他规范
- 当前端需要针对列表中的个别项做特殊处理时,需要后台返回一个供前端判断的 Boolean 字段。
例如后台返回一个选项列表对象数组,前端处理时需要对“全国”做特殊的处理,则需要后台多返回一个字段,如下[ {id:'0',name:'全国',isCountry: true}, {id:'1',name:'北京'}, {id:'2',name:'上海'}, {id:'3',name:'澳门'} ]
字段命名方式可以从业务和展示两个角度来命名。不要使用后台返回的 ID 值做判断。
-
同一个接口的返回值结构应该是固定的,不应因为参数的改变而发生结构上的改变
-
关于数值处理,所有所见即所得的数值建议后端直接保留好返回,如表格、展示数据等,所有需要前端进一步计算处理的数据如 Echarts 数据建议前端处理。
- 相同含义的字段应使用相同的类型,如 学校ID 和 班级ID 若为 String 则均为 String 类型