1.背景
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模版,优雅、快速、随机生成规定数量的数据。
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第二代
相关链接:
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的用户手册
值得注意的是,任何人的操作动作都有所记录,可以点击“首页”查看。
使用感受:RAP2界面简洁、交互友好、上手快,可界面编辑API,不过一个个定义接口返回字段需要花费较长时间。
3.3本地部署RAP2
如果担心使用线上RAP2可能会外泄公司内部的接口文档,可以git clone RAP2的项目,在公司内部搭建个RAP2。具体方法笔者还没有尝试,这里放些参照文章。
https://github.com/thx/RAP/wiki
https://www.cnblogs.com/operationhome/p/10038469.html
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文档,导出离线版文档,功能完善,不过没有开源。
6.小结
在前后端分离的架构中,为了让前端同学在前后端联调时摆脱“等接口-改接口-调接口”的尴尬境地,减少双方的沟通成本、时间成本,提高项目整体开发效率,缩短开发周期。前端同学可以模拟数据,具体可以前端本地模拟数据,或者使用一些前端接口模拟工具。至于哪种方法更好,还需要视具体情况而定。比如一个比较小的项目,前端本地模拟数据足矣,如果为了一个小项目,还需要本地搭建一套接口模拟工具,倒有些“杀鸡用牛刀”了。不过长远而看,使用前端接口模拟工具会带来很多的便利,值得我们花时间去了解和使用