1.1 背景介绍
前后端分离的架构中, 前后端同学约定好接口后就可以并行开发, 最后双方再进行接口的联调. 不过实际开发时, 前后端联调会遇到下面这些问题, 这些问题无疑中会影响联调的效率, 拉长整个开发的周期.
1.2 联调的痛点
"等接口"
前端和后端开发的进度不一致, 例如前端同学已经按照交互完成了页面的开发, 但是后端同学此时还不能及时提供出接口, 此时前端同学会陷入 "等接口" 的境地.
"改接口 - 调接口"
好不容易等来了接口, 前端同学联调后发现接口存在一些问题, 再把问题反馈给后端同学. 后端同学本地复现问题, 修改完接口后再重新部署, 然后通知给前端同学. 前端同学这时再重新调用, 测试接口是否正常, 如果又发现了新的问题, 再重复上面的流程...
首先对于第一个 "等接口" 的痛点, 只有后端同学及时提供接口才能从根本上解决, 比如让后端同学先开发, 前端同学稍后再介入开发. Sadly, 现实很多模块都是前后端同学同时开发, 碰到这种情况, 我们除了 "死等到底", 还可以 "提前准备". 前后端同学可以在开发前, 约定好接口, 具体的形式可以有:
口头约定
=> 看似省事, 实则后患无穷. 可能导致双方记不清细节, 以及日后出现问题后, 容易互相甩锅.
接口文档
=> 写 Word,Markdown 文档, 更新到公司的 wiki 上. 更好一点的话, 使用 swagger 生成文档.
约定好接口后, 前后端同学并行开发. 后端如若不能及时提供接口, 前端可以本地模拟数据或者使用一些接口模拟工具, 详细内容会在后面介绍.
针对第二个痛点, 前端其实可以进行 "工作转移". 具体就是前端同学本地开发完, 确认好各个接口已经按照接口文档约定的参数传参后, 无需做后端同学的陪练, 可以把最新代码发布到某一个开发环境, 让后端同学在写完接口后, 在开发环境通过页面进行联调. 服务端接口如果有问题, 后端同学自己修改完重新发布后, 再自己从前端页面测试. 如果实在需要更改或再增加参数, 再去找前端同学. 这样前端同学就可以从 "改接口 - 调接口" 的循环圈中解脱出来, 把更多精力地放在开发工作上.
2. 前端本地模拟数据
本节三种姿势介绍如何本地模拟数据, 如果不需要可以跳过.
2.1 直接在业务代码里模拟数据
在业务代码里面使用假数据供页面使用, 等到后端提供接口后, 再将页面的假数据注释掉, 改成调用接口.
缺点:
这样会导致业务代码里面混杂了大量冗余代码;
2.2 本地请求 JSON
把假数据放到 JSON 文件中, 本地请求 JSON 文件.
示例(数据为假数据):
- {
- "Code": 200,
- "Message": "",
- "Data": {
- "items": [{
- "exp1": "fbcc7d9e-9be1",
- "exp2": "xxx",
- "enabled": true,
- "size": 0,
- "createBy": "",
- "createTime": "2019-07-12T02:56:54.17+08:00",
- "updateBy": "",
- "updateTime": "2019-08-06T03:03:37.859+08:00",
- "comment": ""
- }
- ],
- "totalCount": 1
- }
- }
业务代码里:
import jsonData from './data.json';
通过 jsonData(例如 jsonData.Data.items)即可获取到 JSON 数据
优点: 比上一个方法好一些, 因为没有直接在业务代码里写入大量的假数据.
缺点: 如果希望模拟 50 条数据的返回, 简单粗暴地硬写 50 条 (大量复制粘贴 + 修改) 又有些浪费时间, 不够优雅.
2.3 使用 Mock.JS
使用 Mock.JS, 按照 Mock 模版生成指定数量的随机数据
2.3.1 在前端新建一个 data.jsx
- import Mock from 'mockjs';
- var responseData = Mock.mock({
- Code:200,
- Message:"",
- Data:{
- 'items|10': [{
- "exp1": /[a-z][0-9]{8}-[0-9]{4}/,
- "exp2|1": [ "xxx", "xxx", "xxx"],
- "enabled|1": [true, false],
- "size": 0,
- "createBy":"@cname",
- "createTime": "@now(yyyy-MM-dd) @increment(1):00:00",
- "updateBy": "",
- "updateTime": "@now(yyyy-MM-dd) @increment(1):01:00",
- "comment": ""
- }],
- "totalCount": 10
- }
- })
- export default responseData;
简单解释下:
这里是 mock.JS 的语法,'items|10':{xxx}表示: items 是一个数组, 里面包含了 10 个对象, 每个对象都包含 exp1,exp2 等属性. 其中 exp1 返回一个按照正则表达式生成的字符串."exp2|1":[]是从数组中随机选一个元素, 作为 exp2 最后的属性值.
[补充] 一些基本的 Mock 知识
Mock 方式:
硬编码
拦截请求:
代码拦截
代码工具(Fiddler,Charles 等)
Mock.JS 规范:
(1)数据模版定义规范(DTD)
数据模板中的每个属性由 3 部分构成: 属性名, 生成规则, 属性值:
- // 属性名 name ; 生成规则 rule ; 属性值 value
- 'name|rule':value
其中属性值类型:
String,Number,Boolean,Object,Array,Function,RegRxp
生成规则 (可选) 有 7 种格式:
- 'name|min-max': value
- 'name|count': value
- 'name|min-max.dmin-dmax': value
- 'name|min-max.dcount': value
- 'name|count.dmin-dmax': value
- 'name|count.dcount': value
- 'name|+step': value
dmin 最少小数位, dmax 最多小数位
step 递增
dcount 固定位数的小数位
(2)数据占位符定义规范(DPD)
格式:@占位符
例如 @cname,@date,@image 等, 不做展开了.
了解更多关于 mock.JS:
mock.JS 网址 http://mockjs.com/
Mock.JS 语法规范文档
这样可以方便得按照我们定义的 Mock 模版, 优雅, 快速, 随机生成规定数量的数据.
2.3.2 或写在 web server
以 egg 为例, 在 controller 这里, 把本来调用真实后端接口注释掉, 用 Mock 数据:
- * getxxxList() {
- let ctx = this.ctx;
- try {
- let responseData = Mock.mock({
- 'items|10': [{
- "exp1": /[a-z][0-9]{8}-[0-9]{4}/,
- "exp2|1": ["xxx", "xxx", "xxx"],
- "enabled|1": [true, false],
- "size": 0,
- "createBy": "",
- "createTime": "@now(yyyy-MM-dd) @increment(1):00:00",
- "updateBy": "",
- "updateTime": "@now(yyyy-MM-dd) @increment(1):01:00",
- "comment": ""
- }],
- "totalCount": 10
- })
- ctx.body = {
- Code: 200,
- Data: responseData,
- Message: ""
- };
- } catch (ex) {
- this.logger.error('Execute xxx Failed:%j', ex);
- ctx.body = {
- Code: 100,
- Data: null,
- Message: "xxx 接口发生错误"
- }
- }
- }
优点:
前后端分离, 用法简单, 方便扩展, 通过随机数据可以模拟各种场景.
缺点:
修改接口时不能前后端同步: 如果在双方并行开发时, 后端又修改了某个字段, 需要及时知会前端, 让前端同学去相应修改本地的 Mock 模版. 不能做到前后端协同一次修改, 即可用. 这也是所有前端本地模拟数据不可避免的弊端.
不能模拟出根据不同请求参数, 返回不同结果的情况.
本节介绍了三种姿势本地模拟数据, 可以说本地模拟数据是种简单直接的解决方法, 可以满足基本的开发需要, 不过很多开发过程中要考虑的情况, 仅仅依靠前端同学模拟数据, 是不足以解决的. 下面就介绍一些 mock 工具. 这些工具能够确保前端在开发过程中的模拟数据可控, 且在使用之后不会对前端或者服务端的流程有任何影响.
常见的前端接口模拟工具有 RAP2,EasyMock,NEI,YApi,Apiary 等, 这些工具基于 Mock.JS 来进行数据模拟, 并在此基础上做了不同的扩展. 这里重点介绍 RAP2 和 Easy Mock, 其他常见前端接口模拟工具的特性也会在后面列出.
3.RAP2
前后端分离开发, Mock.JS 可以解决前端依赖后端提供接口后, 才能请求数据的限制. 不过信息同步的问题随之产生, 可能出现后端修改接口, 前端没有同步的情况. RAP2 则是将前端和后端拉到一个团队仓库, 通过共享一个仓库, 让前端和后端双方同学都可以进行管理, 同步性好, 并且也可以查看接口修改的记录.
3.1 RAP2 是什么
阿里妈妈 MUX 前端团队出品的开源接口管理工具 RAP 第二代
相关链接:
RAP2github 地址 https://github.com/thx/rap2-delos
3.2 使用 RAP2
第一次使用 RAP2, 会首先让你邮箱注册, 注册成功后, 就可以开始新建仓库了. 不过在正式创建仓库前, 这里先介绍一些基本的概念:
仓库: 放置接口文档的仓库, 可以包含多个接口文档
协同仓库: Mock 服务协同仓库, 在当前仓库中无法匹配到接口时, 将会从协同仓库中寻找
团队: 团队可包含多个仓库, 用户可加入多个团队
插件: 用于实现生成 Mock 数据, 拦截真实 I/O 请求以 Mock 数据替换等功能的插件
平台 API: 以开放 API 形式将接口文档, Mock 数据等内容, 提供给外部调用
Mock 模板: Mock.JS 规则模板, 用于生成 Mock 数据, 模板中可定义丰富的规则以适应数据的按需随机性
Mock 数据: 通过 Mock 模板生成的最终 Mock 数据
新建一个仓库, 填写一些基本信息: 名称, 简介, 成员, 协同仓库; 即可以看到建好的仓库:
点击仓库名即可进入仓库, 仓库中已经初始化了一个示例模块, 示例模块中有一个示例接口, 可以扫一眼示例接口, 这些可以帮助新人快速上手.
下面可以自己新建一个模块(示例模块右边, 可新建模块), 然后在新模块中一个接口:
接下来可以设置请求参数和响应内容, 其中请求参数, 响应内容有两种添加方式, 可以逐个添加字段或者采用导入的方式:
采用导入的方式, 这里导入:
- {
- "id": "@id",
- "cname": "@cname",
- "string": "@string(11)",
- "float": "@float(0,10)",
- "int": "@integer(60,70)",
- "boolean": true,
- "array|2": [
- {
- "id": "@integer(1,10)",
- "name": "cname"
- }
- ],
- "actionType|1": [
- "click_url",
- "open_resource_detail",
- "open_resource_search"
- ],
- "title": "@ctitle",
- "city": "@city",
- "email": "@email",
- "ip": "@ip",
- "url": "@url",
- "cfirst": "@cfirst",
- "clast": "@clast",
- "cword": "@cword('123456')",
- "csentence": "@csentence(1,5)",
- "csentence5": "@csentence(5)",
- "cparagraph": "@cparagraph(1,3)"
- }
导入后可以看到:
在接口页面, 通过点击 "插件" 可以看到仓库, 在线编辑的地址, 请求接口地址, 前端直接用 jQuery 的 Ajax 请求线上接口地址就能看到返回结果.
另外 RAP2 提供了 Mock 插件, 不过目前只支持 Kissy 和 jQuery. 使用时需要在项目中加上一行插件代码:
<script type="text/javascript" src="http://{{domainName}}/rap.plugin.js?projectId={{projectId}}&mode={{mode}}"></script>
具体使用规则可查阅 RAP2 的用户手册 https://github.com/thx/RAP/wiki/user_manual_cn
值得注意的是, 任何人的操作动作都有所记录, 可以点击 "首页" 查看.
使用感受: RAP2 界面简洁, 交互友好, 上手快, 可界面编辑 API, 不过一个个定义接口返回字段需要花费较长时间.
3.3 本地部署 RAP2
如果担心使用线上 RAP2 可能会外泄公司内部的接口文档, 可以 Git clone RAP2 的项目, 在公司内部搭建个 RAP2. 具体方法笔者还没有尝试, 这里放些参照文章.
- https://github.com/thx/RAP/wiki
- 4.Easy Mock
杭州大搜车无线架构团队提供的一个可视化, 并且能快速生成 模拟数据 的持久化服务.
特性:
支持接口代理
支持快捷键操作
支持协同编辑
支持团队项目
支持 RESTful, 例如 "/xxxx/:id", 可通过_req.params.id 来获取到参数的值.
支持 Swagger | OpenAPI Specification (1.2 & 2.0 & 3.0)
基于 Swagger 快速创建项目
支持显示接口入参与返回值
支持显示实体类
支持灵活性与扩展性更高的响应式数据开发
支持自定义响应配置(例: status/headers/cookies)
支持 Mock.JS 语法
支持 restc 方式的接口预览
了解更多: https://github.com/easy-mock/easy-mock
直观感受:
可以使用 EasyMock 的扩展语法, 通过添加 function 来返回响应式数据, 示例:
- {
- "data": {
- "age|1-100": 16,
- "province": "@province",
- "city": "@city",
- "intro": "@cparagraph",
- "default": 'shenzhen',
- "address": function({
- _req,
- Mock
- }) {
- if (_req.query.name === 'Peter') {
- return _req.query.name + 'lives in' + Mock.mock('@city')
- } else {
- return this.default
- }
- }
- }
- }
可以预览下返回:
这样就可以根据不同的请求参数, 返回响应式数据.
5. 其他工具
5.1Nei
NEI 是网易杭研前端技术部推出的一款产品, 功能较 RAP2 要丰富, 不过没有开源.
具体提供的功能有:
项目管理: 动态, 团队管理, 权限管理, 项目文档等
页面管理: 项目中的页面定义
异步接口: 可以定义请求头, 请求数据, 发送规则, 响应头, 响应结果, 接收规则等
接口测试和用例管理: 方便回归测试和生成测试代码
数据模型: NEI 中最强大的功能之一, 对应数据库中的实体对象
页面模板: NEI 配套的工具构建工具会根据定义生成模板文件
规则函数: 自定义 MOCK 数据, NEI 也预置了常见的规则函数
业务分组: 按照业务对项目资源进行细分, 方便管理
工程规范: 本身可以当作脚手架, 也可以和 NEI 项目结合, 集成项目中的 API 和数据模型
消息中心: 保证重要的操作能及时通知到相关负责人
了解更多, https://zhuanlan.zhihu.com/p/23191873
5.2YApi
YApi 是去哪儿网移动架构组开发的一个开源项目
特性:
基于 Json5 和 Mockjs 定义接口返回数据的结构和文档, 效率提升多倍
扁平化权限设计, 即保证了大型企业级项目的管理, 又保证了易用性
类似 postman 的接口调试
自动化测试, 支持对 Response 断言
MockServer 除支持普通的随机 mock 外, 还增加了 Mock 期望功能, 根据设置的请求过滤规则, 返回期望数据
支持 postman, har, swagger 数据导入
免费开源, 内网部署, 信息再也不怕泄露了
了解更多: https://github.com/YMFE/yapi
使用感受: 直观感受功能比 RAP2 更丰富, 不过操作不如 RAP2 顺畅, 比如编辑时不能实时看到数据, 要点击 "预览" 才能查看.
5.3 Apiary
可以在线模拟测试, 因为该平台具备模拟服务器测试服务. 可以把设计好的程序在线测试, 验证. 可以快速生成 API 文档, 导出离线版文档, 功能完善, 不过没有开源.
了解更多: https://app.apiary.io/
6. 小结
在前后端分离的架构中, 为了让前端同学在前后端联调时摆脱 "等接口 - 改接口 - 调接口" 的尴尬境地, 减少双方的沟通成本, 时间成本, 提高项目整体开发效率, 缩短开发周期. 前端同学可以模拟数据, 具体可以前端本地模拟数据, 或者使用一些前端接口模拟工具. 至于哪种方法更好, 还需要视具体情况而定. 比如一个比较小的项目, 前端本地模拟数据足矣, 如果为了一个小项目, 还需要本地搭建一套接口模拟工具, 倒有些 "杀鸡用牛刀" 了. 不过长远而看, 使用前端接口模拟工具会带来很多的便利, 值得我们花时间去了解和使用.
来源: https://www.qcloud.com/developer/article/1493249