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项目不做要求
- 账号、密码
账号、密码禁止明文传输,如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
- 手机号、银行卡号、姓名等私密信息
- 通用加密策略:
Hash(Constant-SALT + Pasword)
Random-Salt为每个应用or业务系统的随机盐,与其他细节加密字段进行Hash运算后再跳转。 - 数字加密策略:
- 转换进制,如:
// 加密 const mask = (+phone).toString(28) // 解密 const phone = parseInt(mask, 28)
- 转换进制,如:
- 手机号、银行卡号加密策略:
- 方法一
参考上文数字加密策略 - 方法二
参考上文通用加密策略
- 方法一
- 姓名、邮箱、住址等加密策略:
参考上文通用加密策略
- 第三方跳转时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 |