前后端协作规范

4,298 阅读15分钟

1 协作流程规范

1.需求分析。确保大家对需求有一致的认知
2.设计接口文档。前端需要确认是否符合要求
3.并行开发。前端需要根据接口文档进行Mock, 模拟对接后端接口;联调之前,要求后端做好接口测试
4.真实环境联调。前端将接口请求代理到后端服务,进行真实环境联调。

2 接口规范

2.1 接口风格:

  • RESTful —— 推荐
  • JSONRPC

2.1.1 RESTful

  • 标准格式

    http(s)://server.com/app-name/{version}/{domain}/{rest-convention}
    
  • 用法

    • GET
    GET /api/teams (对应团队列表)
    GET /api/teams/123 (对应 ID 为 123 的团队)
    GET /api/teams/123/members (对应 ID 为 123 的团队下的成员列表)
    GET /api/teams/123/members/456 (对应 ID 为 123 的团队下 ID 为 456 的成员)
    
    • POST

    用于创建资源,或者特殊资源提交(如文件上传/下载)

    POST /api/teams/123/members/456 (新增ID 为 123 的团队下 ID 为 456 的成员信息)
    body参数{name:'xxx'}
    
    POST /api/teams/123/members/456 (下载ID 为 123 的团队下 ID 为 456 的成员信息)
    
    POST /api/teams/123/members/456 (上传ID 为 123 的团队下 ID 为 456 的成员信息)
    body参数{file:formData}
    
    
    • UPDATE(PUT和PATCH)

    PUT 用于更新资源的全部信息,在请求的 body 中需要传入修改后的全部资源主体
    PATCH 用于局部更新,在 body 中只需要传入需要改动的资源字段。

    POST /api/teams (创建团队)
    POST /api/teams/123/members (创建团队ID 为 123 的成员)
    PUT /api/teams/123/members/456 (更新ID 为 123 的团队下 ID 为 456 的成员全部信息)
    PATCH /api/teams/123/members/456 (更新ID 为 123 的团队下 ID 为 456 的成员部分信息)
    
    • DELETE
    // 单个删除
    DELETE /api/users/123
    
    // 批量删除
    DELETE /api/users
    body参数{ids:[]}
    
    • HEAD

    获取一个资源的元数据,如数据的哈希值或最后的更新时间。

    • OPTIONS

    获取客户端能对资源做什么操作的信息

    • 过滤
    ?limit=10:指定返回记录的数量
    
    ?offset=10:指定返回记录的开始位置。
    
    ?page=2&size=100:指定第几页,以及每页的记录数。
    
    ?sortby=name&order=asc:指定返回结果按照哪个属性排序,以及排序顺序。
    
    ?keyname=1:指定筛选条件
    
  • 其他规范:
    规则1:URI结尾不应包含(/)
    规则2:正斜杠分隔符(/)必须用来指示层级关系
    规则3:应使用连字符(-)来提高URI的可读性
    规则4:不得在URI中使用下划线(_)
    规则5:URI路径中全都使用小写字母

    具体详见:RESTful 架构详解

2.2 注意点:

  • 明确数据类型。例如数字应为Int或Float类型,而非String类型
  • 明确空值的意义。比如在做更新操作时,空值是表示重置,还是忽略更新
  • 明确默认值。对象类型的返回空对象{}, 数组类型的返回空数组[],字符串类型的返回空字符串"",Number和Boolean类型的返回null
  • 对于大数字(如Java的long 类型,整数类型大于16位浮点数类型大于18位),返回给前端时需要设置为字符串类型, 防止js发生溢出
  • 关于日期时间格式
    • 对于需要前端再次处理的日期值, 可以使用时间戳
    • 对于纯展示用的日期值, 推荐返回格式化文本, 例如: 2017年1月1日或2017-01-01 20:00:00
    • 对于日期时间需要排序时,返回时间戳或者格式化的包含毫秒数的文本
    • 建议同时返回时间戳的原始值(或 ISO 标准格式)和用于统一显示的格式化文本,例如: {"createTime": 1543195480357, "createTimeText": "2018年11月26日"}
  • 关于分页
    • 分页计数,从1开始
    • 避免滚动加载可能出现的重复数据,采用 lastId 分页方式
  • 图片URL
    • 图片的 URL 建议返回完整的 URL,协议为HTTP时可省略协议
  • 文件上传/下载
    • 文件上传限制默认最大为10M
    • 文件上传类型默认支持:
      • 文档类:doc、docx、xls、xlsx、ppt、pptx、pdf、txt
      • 图片类:jpg、jpeg、png、gif
      • 其他:zip、rar
    • 多文件上传不支持IE9及以下版本浏览器
    • 图片上传服务器前需先在前端压缩,默认控制在400万以下像素或2560*1440及以下分辨率
  • 响应避免冗余的嵌套
  • 接口版本化,保持向下兼容;接口不兼容时需升级版本
  • 建议使用Swagger等工具,避免人工维护可能导致的代码和文档不同步
  • 鉴权通过header实现
  • 同时给出 ID 字段和用于显示字段时,前端提交只提交ID字段

2.3 接口文档规范

后端通过接口文档向前端暴露接口相关的信息。通常需要包含这些信息:

  • 版本号
  • 文档描述
  • 服务的入口,例如协议、域名、基本路径
  • 测试服务器,可选
  • 简单使用示例
  • 安全和认证
  • 具体接口定义
    • 方法名称或者URI
    • 方法描述
    • 请求参数及其描述,必须说明类型(数据类型、是否可选等)
    • 响应参数及其描述, 必须说明类型(数据类型、是否可选等)
    • 可能的异常情况、错误代码、以及描述
    • 请求示例,可选

2.3.1 Header

  • Content-Type
    • application/json 默认
    • application/x-www-form-urlencoded 表单提交/APP请求
  • Authorization
    • Basic XXXXXXXX
    • Bearer XXXXXXXX
  • Cookie
  • openid = hash(appid+uid)
  • Expires 过期时间,默认为0
  • Referer 之前页面URL,跳转第三方地址需要此参数
  • lang 多语言
    • zh_cn
    • zh_hk
    • en_us

2.3.2 请求参数

  • 4种类型:
    1、cookie: 一般用于OAuth认证
    2、Request Header: 一般用于OAuth认证
    3、请求body数据
    4、地址栏参数: 详见【2.1.1过滤】

2.3.3 响应参数

  • response Demo
    * 表示不必须
    {
       status: 200,               // 详见【status】
       data: {
          code: 10000,            // 返回码,详见【4.3.2 公共响应参数】
          msg: '成功',            // * 返回码描述
          sub_code: 'success',    // 明细返回码
          sub_msg: '',            // * 明细返回码描述,显示给客户端用户【须语义化中文提示】,code不为10000时均需toast提示 
          sysMsg: '',             // * 提示,调试使用,可包括中英文数字及特殊符号
          result: [],             // 返回的数据结果对象,可根据需要定义
          page: {
            current: 1,           // * 当前页,从1开始
            size: 10,             // * 每页记录数
            total: 100            // * 总记录数
          }
       },
       message: '成功',           // 提示,显示给客户端用户【须语义化中文提示】
       sysMessage: 'success'      // * 提示,调试使用,中英文都行
    }
    

具体请参照OpenAPI 规范摘要OpenAPI 3.0规范及【4.3 公共参数】

2.4 转义与编码

编码规则:encodeURIComponent 与 decodeURIComponent

  • URI特殊字符需转义
    • 特殊字符及转义符号

      特殊字符 转义后的符号
      + %2B
      空格 %20
      / %2F
      ? %3F
      % %25
      # %23
      & %26
      = %3D
      ! %21
    • 第三方回调页面URI需反转义,如:支付宝支付成功回调页面

  • URI汉字需转为Unicode码

2.5 加密

  • 首选HTTPS,其中以下项目要求必须为HTTPS
    • APP
    • 微信公众号和小程序
    • 使用第三方API(如海康、高德等)
  • HTTP项目需要对关键敏感信息进行加密处理,HTTPS项目不做要求
    1. 账号、密码
      账号、密码禁止明文传输,如admin等
    • 密码加密策略:
      • 方法一
        服务器端存储密码为Hash(Random-Salt + Hash(Constant-SALT + Pasword))
        客户端传输密码为Hash(Constant-SALT + Password)
        Random-Salt为用户的随机盐,每个用户均不同,服务器端收到客户端的hash密码后,在将其与用户随机盐一起Hash运算,从而将结果与数据库存储值相比较。
        优点:数据库存储的密码经过两层哈希,且其中包含了固定盐和用户随机盐,安全性较高。
        缺点:客户端传输的密码虽然经过了一层哈希,但是使用固定盐值,因此相同密码生成的结果均一致,容易收到重放攻击。
      • 方法二
        服务器端存储密码为Hash(Constant-SALT + Pasword)
        客户端传输密码为Hash(Random-Salt + Hash(Constant-SALT + Password))
        Random-Salt为每次进入登陆页面时生成的随机盐,此处主要是为了防止重放攻击,作用类似与nonce 服务器收到客户端上传的hash之后的密码,根据用户ID取出服务器存储的密码,并通过与之前返回客户端的Random-Salt(可存储于Session中)进行Hash运算,从而与客户端上传的密码进行比较。
        优点:客户端有效的防止了重放攻击。
        缺点:在前端代码中,固定盐值暴露,一旦后台数据库被攻破,用户密码信息将有危险。
    • 加密库:
      • MD5
      • crypto-js
    1. 手机号、银行卡号、姓名等私密信息
    • 通用加密策略:
      Hash(Constant-SALT + Pasword)
      Random-Salt为每个应用or业务系统的随机盐,与其他细节加密字段进行Hash运算后再跳转。
    • 数字加密策略:
      • 转换进制,如:
        // 加密
        const mask = (+phone).toString(28)
        // 解密
        const phone = parseInt(mask, 28)
        
    • 手机号、银行卡号加密策略:
      • 方法一
        参考上文数字加密策略
      • 方法二
        参考上文通用加密策略
    • 姓名、邮箱、住址等加密策略:
      参考上文通用加密策略
    1. 第三方跳转时URI加密
    • 加解密策略
      参考上文通用加密策略

2.6 跨域

  • CORS 实现

3 接口测试与模拟

3.1 工具:

  • RESTful
    • Swagger 这是最为接近上面理想模型的一个解决方案
    • JSON Server 快速生成JSON mock服务器
    • Easy Mock 可视化的、在线的接口mock服务
    • Yapi接口平台 支持本地部署
  • GraphQl
    • GraphQL Faker
    • graphql-tools
  • 模拟数据生成
    • faker.js 强大的模拟数据生成工具,支持Node和浏览器
    • Mock.js 数据生成和模拟工具

4 附录

4.1 公共状态码(status)

status message 适用请求方式 适用情形
200 OK GET 服务器成功返回用户请求的数据,该操作是幂等的(Idempotent)
201 CREATED POST/PUT/PATCH 用户新建或修改数据成功
202 Accepted * 表示一个请求已经进入后台排队(异步任务)
204 NO CONTENT DELETE 用户删除数据成功
400 INVALID REQUEST POST/PUT/PATCH 用户发出的请求有错误,服务器没有进行新建或修改数据的操作,该操作是幂等的
401 Unauthorized * 表示用户没有权限(令牌、用户名、密码错误)
403 Forbidden * 表示用户得到授权(与401错误相对),但是访问是被禁止的
404 NOT FOUND * 用户发出的请求针对的是不存在的记录,服务器没有进行操作,该操作是幂等的
406 Not Acceptable GET 用户请求的格式不可得(比如用户请求JSON格式,但是只有XML格式)
410 Gone GET 用户请求的资源被永久删除,且不会再得到的
422 Unprocesable entity POST/PUT/PATCH 当创建一个对象时,发生一个验证错误
500 INTERNAL SERVER ERROR * 服务器发生错误,用户将无法判断发出的请求是否成功

4.2 公共错误码(code)

code(返回码) msg (返回码描述) sub_code (明细返回码) sub_msg (明细返回码描述) 解决方案
10000 接口调用成功,调用结果请参考具体的API文档
20000 服务不可用 unknow-error 服务暂不可用 请稍后重试
20001 授权权限不足 invalid-auth-token 无效的访问令牌 请刷新授权令牌或重新授权获取新的令牌
auth-token-time-out 访问令牌已过期 请刷新授权令牌或重新授权获取新的令牌
invalid-app-auth-token 无效的应用授权令牌 请刷新应用授权令牌或重新授权获取新的令牌
invalid-app-auth-token-no-api 未授权当前接口 请重新授权获取新的应用授权令牌
app-auth-token-time-out 应用授权令牌已过期 请刷新应用授权令牌或重新授权获取新的令牌
20002 用户信息验证失败 invalid-auth-login 登录信息验证未通过 请输入正确的用户名和密码
invalid-auth-user 无效的用户信息 请输入正确的用户信息
missing-auth-user 无法获取用户信息 请重试
40001 缺少必选参数 missing-method 缺少方法名参数 请求参数里面必须要有method参数
missing-signature 缺少签名参数 请检查请求参数,缺少sign参数
missing-timestamp 缺少时间戳参数 请检查请求参数,缺少timestamp参数
missing-version 缺少版本参数 请检查请求参数,缺少version参数
decryption-error-missing-encrypt-type 解密出错, 未指定加密算法 请检查参数,缺少encrypt_type参数
40002 非法的参数 invalid-parameter 参数无效 请检查参数,格式不对、非法值、越界等
upload-fail 文件上传失败 文件写入失败,请重试
invalid-file-extension 文件扩展名无效 请检查传入的文件扩展名称,目前支持格式:csv,txt,zip,rar,gz,doc,docx,xls,xlsx,pdf,bmp,gif,jpg,jpeg,png
invalid-file-size 文件大小无效 请检查文件大小,目前支持最大为:20MB
invalid-method 不存在的方法名 请检查入参method是否正确
invalid-format 无效的数据格式 请检查入参数据格式
invalid-timestamp 非法的时间戳参数 时间戳参数timestamp非法,请检查格式需要为"yyyy-MM-dd HH:mm:ss"
invalid-charset 字符集错误 请求参数charset错误,目前支持格式:GBK,UTF-8
invalid-signature 无效签名 1.公私钥是否是一对
2.检查公钥上传是否与私钥匹配
3.存在中文需要做urlencode
4.签名算法是否无误
invalid-token 无效令牌 auth_token 为无效的令牌,请确认令牌有效
invalid-encrypt-type 无效的加密类型 请检查入参encrypt_type,目前只支持AES
invalid-encrypt 解密异常 请重试
decryption-error-not-valid-encrypt-key 解密出错, 未配置加密密钥或加密密钥格式错误 没有配置加密密钥
missing-signature-config 验签出错, 未配置对应签名算法的公钥或者证书 没有配置应用公钥
not-support-app-auth 本接口不支持第三方代理调用 本接口不支持第三方代理调用
40004 业务处理失败,需要返回具体的失败信息
60000 前端访问失败
60001 设备不可用 invalid-device-type 无效的设备类型 请更换设备重试
invalid-wechat-device 无效的微信客户端 请在微信客户端打开链接
invalid-wework-device 无效的企业微信客户端 请在企业微信客户端打开链接
invalid-alipay-device 无效的支付宝客户端 请在支付宝客户端打开链接
invalid-mobile-device 无效的移动客户端 请在移动设备打开链接
invalid-pc-device 无效的PC客户端 请在电脑端打开链接
60002 浏览器不可用 invalid-browser-version 无效的浏览器版本 请升级至最新版本浏览器重试
invalid-browser-type 无效的浏览器类型 请更换指定浏览器重试
60005 鉴权失败 invalid-auth-token 无效的访问令牌 请刷新授权令牌或重新授权获取新的令牌
60006 无路由权限 invalid-route-auth 无相应页面的访问权限 找不到对应的页面,请返回重试
60007 无操作权限 invalid-handle-auth 无相应功能的操作权限 请咨询应用运维人员
60008 获取信息失败 missing-person-info 无法获取个人信息 获取个人信息失败,请重试

4.3 公共参数

  • 4.3.1 公共请求参数
参数 类型 是否必填 最大长度 描述 示例值
charset String 10 请求使用的编码格式,如utf-8,gbk,gb2312等 utf-8
version String 3 调用的接口版本,默认为:1.0 1.0
size Int 2 分页长度 10
current Int - 当前页码 1
  • 4.3.2 公共响应参数
参数 类型 是否必填 最大长度 描述 示例值
code Int 5 网关返回码 40004
msg String - 网关返回码描述 Business Failed
sub_code String - 业务返回码,参见具体的API接口文档
sub_msg String - 业务返回码描述,参见具体的API接口文档
result Object / Array - 返回的数据对象
sign String - 签名
version String 3 调用的接口版本,默认为:1.0 1.0
total Int - 记录总数 2000
size Int 2 分页长度 10
current Int - 当前页码 1