从前端角度谈一谈如何优雅地设计、维护接口

2021/02/20

前段时间接受了公司内的一个老项目,由于项目之前的开发工期很紧张,导致项目整体质量不高。 因此计划重构一下老项目,重构过程中遇到许多原先接口设计上的不合理导致的坑,因此决定写点什么,聊一聊接口设计规范和接口文档的维护。

分为两部分来讨论 1. 接口设计 2. 接口维护 3. 接口与开发相结合

一. 接口设计

“接口”顾名思义,是前后端用来交互的桥梁,在设计的过程中既要考虑到前端也要考虑到后端,因此开发的过程中一定是 “文档先行,开发后置” 的原则。

1. 谁来制定文档?

接口文档应该由后端编写还是由前端编写这个问题,不同的公司有不同的处理方案。

个人建议 由前端设计接口路由与结构,后端设计字段名称 。在设计的过程中,前后端需充分讨论评估。 因为接口的路由和结构与前端交互息息相关,如果完全交由后端来设计,可能会出现同一业务多次调用接口的问题,影响用户体验。而接口字段则是与数据库字段息息相关,而前端则不太需要关心,因此交给后端设计较为合理。

当然,不同的场景适用不同的方式,还是要具体问题具体分析。比如开发的产品是一个偏服务类型的产品,那么将接口设计交给后端开发则更为合理。

2. 是否要用 RESTful 风格?

个人感觉 RESTful 风格 较为适合仅包含 “增删改查” 等基本业务的小项目,复杂项目不建议,原因很多:

  1. RESTful 将参数分散到 请求方式、路由本身、路由参数、请求参数四个地方,不方便
  2. 请求方式 post get delete patch put 动词过少
  3. 状态码和实际业务差距大 等等

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 类型

文章导航