分享 | 前后端分离架构下的前端模拟经验(吐血整理)

在现在流行的“前后端分离”架构中,前后端的联调,是一道必不可少的工序,通常在不借助任何工具的情况下,进行前后端联调大概率会出现以下几种尴尬的局面:

1.前后端开发速度不一致,前端页面写好之后,必需要等待服务端的接口出来,才能有足够的数据支撑UI的显示。

2.服务端接口不能满足界面的需求时,要前端调用发现问题之后,反馈给服务端,然后服务端需要根据要求修改接口定义和业务逻辑,重新部署,前端再重新调用。

3.由于后端接口常需要根据前端需求变动,因此后端接口的变更难以有效的保证质量,前端调用接口的操作成为了测试接口是否有bug的手段。

以上就是前后端联调过程中最主要的问题,特别是在项目稍微有点规模之后,“等接口,改接口、调接口”似乎成了前端开发工作中的“新常态”,沟通成本,时间成本,服务端部署重启的成本加在一起,就是项目工期经常延后的重要原因。

解决这个问题的办法通常有两种思路,一种从后端服务中实现模拟接口,一种在前端工程上实现模拟数据,因为后端服务实现模拟接口成本比较大,所以在实际应用中通常采用在前端做模拟数据。

1. mock.js

前端模拟的做法一般是在业务代码里面先写上假数据供页面使用,等后端接口写好之后,再将假数据注释掉,改成接口调用,通常表现为以下的形式:

不过这样的做法会在业务代码里面产生非常多的冗余代码,且在真实接口和假数据之间的切换繁琐,非常不利于维护。为了解决这一问题,阿里巴巴开源了一款功能非常全面的模拟数据生成的 JS 库:mock.js,通过它我们可以在前端工程里使用简单的语法模拟各类常见的数据。比如现在页面上需要一个网站和URL的列表,但是后端接口还没有开发好,我们可以利用mock.js来写这样一个结构:

代码中,links|100是mock.js的一个语法,表示的是给links数组里面创建100个对象,每个对象都包含name和url两个属性,这两个属性会通过@name和@url这两个mock.js的规则,随机生成名字和链接(关于mock.js的语法规则可以查看其官网http://mockjs.com)。实践中,一般单独用mock.js写一个data.json的文件,然后在业务代码里面切换真实接口请求和本地data.json文件请求,如下面的代码所示:

也可以直接写真实的接口,然后请求发出去的时候,利用fiddler或者charles等代理工具,将这个接口重定向到本地的data.json文件,这样就不用改动业务代码,更加方便易操作。

mock.js虽然已经简化了前端工作量,但是它只解决了一部分问题。比如说:

1.所有接口的mock规则都需要开发者手动定义;

2.难以模拟出各种不同的网络请求状态;

3.难以模拟出数据错误,或者空数据的状态;

4.不能模拟出根据不同请求参数,返回不同结果的情况。

上面的最后三种状态都是在前端界面的开发过程中都需要考虑到的情况,但是依靠前端的模拟工具比较难以解决,所以这些需求催生了一些后端接口的mock工具。这些工具能够确保前端在开发过程中的模拟数据可控,且在使用之后不会对前端或者服务端的流程有任何影响,使得前后端有一个明确的分界点。常见的前端mock工具有RAP,YApi,NEI,EasyMock,Mock-API等,他们都是基于Mock.js来进行数据模拟的,并在此基础上做了不同的扩展。我们这里简单介绍常见的两款——RAP和YApi:

2. RAP

RAP是阿里团队出的一款接口管理工具,帮助开发人员有效的管理接口文档。包括阿里集团在内的三百五十多个企业都在使用RAP管理重要的接口文档。

下面是一张RAP的一张开发流程图,也基本适用于所有使用mock工具的项目。

从这张图上我们可以看到,项目开发阶段一开始就要先制定接口,并利用mock工具将接口模拟出来,然后前后端分别进行开发,这时候前端是跟mock工具生成的mock数据进行交互的,而后端则开发与测试自己的接口是否正确。在前后端都开发完毕之后,后端再把真实接口部署出来,然后前端把把mock环境切到真实环境,进行联调和测试。由此可以看到,利用mock工具,我们至少能在开发阶段做到真正的前后端分离的状态了 。

RAP的可视化界面(GUI)使得数据模拟操作变得非常简单,但是如果要在项目中用到这些数据的话,还是有一些限制:使用RAP需要在项目中引入一行RAP插件代码,利用这个插件来拦截发出去的请求,然后返回RAP模拟出来的数据,当要切换到真实接口测试的时候只需要改动插件代码上的一个参数就可以了,但是遗憾的是现阶段RAP插件的请求拦截功能只支持KISSY和jQuery,具体使用规则,可以查看其官方文档RAP用户手册。

(网上有大牛提供了AngualrJS版的RAP插件,详见:https://github.com/xiaomaojames/ng-rap)

YApi是去哪儿网移动架构组开发的一个开源项目,相比较RAP,它有以下优点:

  1. 扁平化的权限设计,可以满足大型企业级的项目管理;
  2. 支持快速导入其他常见格式(例如postman,swagger,har)的接口数据,方便快速添加接口;
  3. 具有接口文档管理的功能,YApi将接口文档和测试通过单一数据源连接到一起,避免出现接口改动后文档更新不及时的情况。

YApi在一定程度上来说,比RAP的功能更加完善,也更加强大,它提供接口文档管理,接口数据模拟(Mock),接口调试,自动化测试等一系列功能。

在数金,我们通常使用swagger来管理API接口,虽然YApi支持导入swagger格式的数据,但只能以json文件的形式。当后端同学改动了项目的现有接口,或者增加了一些接口,就需要再重新导出一份swagger文档的json文件,然后再导入解析一次,这样的操作既冗杂又繁琐。因此,我们需要一款支持“一键同步swagger”的操作的工具,EasyMock这款工具便进入了我们的视野。

EasyMock

1. EasyMock介绍

EasyMock是大搜车前端团队开发的一个可视化,能快速生成模拟数据的在线mock服务。它没有RAP那么有限制性,也没有YApi那么完善的功能,但是对于我们来说,它刚刚好,在对比了现有的几种前端开源mock工具后,我们选用了EasyMock作为我们团队的前端模拟工具,并将它部署在数金内网,下面我们简单介绍一下EasyMock的一些特性:

与其他的mock工具一样,EasyMock的语法规则基于Mock.js,能够使用简单的语法创建出模拟数据,除了支持mock.js的基本语法之外,EasyMock还做了一些扩展,例如:

  1. 支持简单的Function,可以实现逻辑判断,并对结果进行动态的返回。
  2. EasyMock在每个mock接口里面注入了一些express的变量,这些变量就是原来 req 上的几乎所有的属性,如_req.header、_req.query等,有了这些变量,就可以根据用户调用mock接口时传进来的信息做非常动态的响应式返回。
  3. EasyMock支持restful风格的接口,例如“/merchandise/:id”,我们可以通过_req.params.id 来获取到参数的值。

除了在mock.js语法的基础上做了扩展之外,EasyMock的另一大优势是可以根据swagger中定义的实体的属性类型生成对应的mock数据。由于我们公司大多使用swagger来管理接口,所以在定义好接口之后,可以先利用接口文档部署一套只有数据结构,没有真正实现功能的接口,然后通过swagger,就可以在EasyMock中生成一个包含此文档地址数据的模拟接口,此操作支持swagger1.0和swagger2.0的语法。当接口有更新的时候,EasyMock支持“一键同步”,且同步操作会先检测这个接口是否有变动,如果有则对这个接口做覆盖式的更新。这一特性对于不断有需求进来,不断迭代的项目非常友好,这也是我们选择这个工具的非常重要的一个原因。

通过EasyMock来进行数据模拟对流程不会产生任何影响,不过,如果计划通过swagger接口文档来生成模拟接口,需要将swagger注解写的更加严谨和完善,这样在mock接口中才能看到更加清晰的看到这个接口的功能,用法等等信息。

2. EasyMock的使用方法

下面我们以使用swagger的项目为例,简单的讲一下EasyMock的使用方法。如果你没有进行私有云部署的话,可以进EasyMock官网https://www.easy-mock.com/login 注册一个账号。新注册的账号会自动生成一个演示项目, 里面创建了多种的Mock类型,对于新手来说,可以先看一下这个演示项目,在此基础上进行编辑。如果你想创建自己的项目,可以跟着以下的步骤来做:
(1)创建项目

点击主页右下角的蓝色加号按钮,进入项目创建界面

(2)填写基本信息

因为我们要基于swagger来创建这个项目,所以注意要在swagger Docs Api里面填入你项目的swagger Docs Api的地址(如果你的项目还没有使用swagger,跳过即可)。

注意:项目swagger Docs Api的地址并不是项目swagger-ui的访问地址,一个通用的获取swagger Docs Api地址的方法就是,将swagger-ui的URL中项目名只有的部分替换成/v2/api-docs,比如地址为https://xxxx.xxxx.com/projectname/swagger-ui.html替换后的地址为https://xxxx.xxxx.com/projectname/v2/api-docs,这样就能得到swagger Docs Api的地址了。

基本信息填写完毕后,点击创建按钮,即可创建成功。接下来回到项目页面就可以看到我们创建的项目:

(3)查看接口信息
点击上图的项目地址,进去之后,你会看到EasyMock将你swagger中的接口都同步过来了。

我们可以看到,上面甚至显示了POST/GET方法,以及一些描述性的文字,这些内容的显示要求写接口的同学定义的更加严谨,swagger注解要写的更加全面。
在接口信息的最右边有四个按钮,分别是预览,编辑,复制地址以及其他操作,点击预览按钮我们可以看到这个接口生成的模拟数据,如果接口是get方法的话,你将会直接看到返回值,但如果是post方法的话,需要你先在右边的输入框里面先输入你的请求参数,然后才能看到返回结果。

因为我们现在并没有写请求参数与返回数据之间的关系,所以不出意外的话,随便输入什么值,都可以得到一个模拟数据。

(4)根据需要完善接口

我们必须要知道请求一个从Swagger上同步到EasyMock的模拟接口,默认是不能直接根据请求入参进行响应式的返回的(否则就不能称之为模拟了),如果要定制化,则需要我们根据EasyMock的扩展语法手动添加一些function来返回响应式数据,所以虽然模拟数据不能直接做到数据联动,但是借助于响应式数据,也能玩出很多花样。

我们可以在数据编辑器中,为某个属性指定一个function。在function中EasyMock提供了_req对象,这个对象我们已经在上面提到过,它是express的一个对象,里面有_req.header、_req.body等属性,这使得我们可以通过这些属性来做响应式数据,一个简单的示例如下:

除了响应式的返回,EasyMock还支持自定义的响应状态,通过在返回的结构中添加_res对象,可以给返回添加定制信息,_res对象里面包含_res.status,_res.cookies,_res.headers以及_res.data等,其中_res.data是在_res.status不为200的时候返回的,用来替代正常返回体里面的data。

至此,我们运用以上简单的四个步骤就可以基于接口的swagger文档创建一个EasyMock的项目。如果接口有更新,点击项目右上角的按钮进行同步就可以了。

项目创建好了之后,就到了使用的环节了,想要使用mock的接口,而不改变业务代码的逻辑,则只需要在发请求的时候用代理工具,例如charles,fiddler等,将对应的请求Map Remote到新建的mock接口就可以了。

EasyMock的适应性改造

虽然说EasyMock功能比较强大,能够匹配状态为200时的响应数据,并且能够查看请求参数和响应参数。但是有时候却不能满足特定的需求。

比如,在数金,服务端的同学会自定义许多的异常状态,响应状态依旧是200,但是会将一些请求的异常码(请求成功,但是因为某些原因不能得到正确结果的情况)根据异常状态返回给前端;所以大部分项目的接口中都定义了响应报文头,用于区分接口请求状态,利用swagger平台管理接口的时候,这部分内容没有直接显示在swagger页面的response中,只有调用接口之后,才会拼接到接口的返回数据中,然后我们对每个接口返回的数据做逻辑处理时,会运用这部分的结果来判断接口的业务处理结果。

而在使用开源版本的EasyMock解析我们的swagger接口的时候,它会把这部分拼接的内容忽略掉,以至于在最后生成的接口中没有自定义响应报文头的部分,这样就和我们真实接口返回的数据接口不一致,所以我们在最新版EasyMock源码上进行了一些改造,让这部分自定义的内容依旧能够拼接到接口的返回数据中。

在改造之前,首先要弄清楚EasyMock中创建项目的流程。EasyMock默认都是使用的swagger地址方式获得API。创建项目时,将项目信息数据传到后端后,若通过检测,会调用project服务以及获取swagger接口文档的服务,其中project服务主要用对项目的增删改查;而swagger服务则是根据前端传来的swagger API地址获取对应的API文档,进行解析后将数据存入redis。在前端页面预览或者调用接口的时候,后端swagger服务会根据参数中的projectId从redis中取出相应的整个项目数据,然后再取出需要的接口数据,取出后使用Mock.js实例化数据,然后将数据返回前端。
搞清楚了项目的关键流程后,我们分别对EasyMock的前后端进行了改造,将接口自定义的“响应报文头”纳入了管理范围。这样,我们就可以在新建项目时,填入自定义的响应报文头数据,然后预览接口时能够看到自定义报文部分经过Mock之后返回数据,如下图所示:

改造后的数金版EasyMock已经部署到了我们的研发测试环境,这个改造主要是为了适配本公司项目的特点,如果你在创建项目的时候不添加这部分的数据或者希望添加其他自定义数据,也完全支持。

EasyMock的使用场景

在数金,我们的EasyMock主要使用在两个场景:

1.  调试页面

在平时的开发中,前端使用EasyMock来调试页面,这样可以避免“等接口”的尴尬局面以及关联系统环境不稳定的情况,使用步骤如下:

  1. 从swagger上面拉下所需要的接口的基本数据结构,并进行微调(比如说指定你想要mock出几条数据等);
  2. 利用代理工具将本地开发的请求Map Remote到EasyMock对应接口的URL上;
  3. 再次发起请求,就可以看到接口被代理到EasyMock的接口上,并返回了mock生成的数据。

2.  做一个稳定的演示Demo

在数金,由于业务需要,我们可能需要经常给客户演示我们的产品,但是如果在演示的过程中关联系统的接口不稳定,请求结果出错,就可能出现演示失败的情况。为了解决这个问题,我们利用EasyMock做了一个专门用于客户演示的App Demo,前端页面依旧是我们正常项目的页面,但是api层调用的是我们EasyMock利用swagger创建的接口。

如上图所示,我们把App客户端配置的webhost地址更改成了我们EasyMock的地址,并在服务端配置仅拦截接口请求而不拦截页面请求,通过这样的操作,我们就能利用EasyMock创建一个环境稳定的Demo App,演示的时候再也不用担心环境问题啦。
以上是目前EasyMock在数金最常用的两个场景,当然EasyMock除了在前端开发的时候起到了重要的作用外,也可以应用在单元测试,自动化测试等场景中。大家可以自行发掘,如果有其他的一些好的场景,欢迎与我们交流、分享。

总结

在前后端的联调过程中,前端接口模拟工具并不是必需的,或许对于一些小的项目来说帮助没有那么明显,但是一旦项目比较繁琐或处于持续迭代中,EasyMock这样的前端接口模拟工具将会给开发者带来很多的便利,将前后端开发人员从泥潭中拯救出来。数金的前端模拟平台刚刚搭建完成不久,我们也是在应用过程中不断的学习。在使用EasyMock的过程中,我们总结了自己的一套应用流程:

  • 分析与明确需求。
  • 前端查看设计稿,确认交互逻辑;服务端根据需求设计数据库和接口。
  • 接口评审与微调。
  • 接口部署开发,并生成到swagger平台,EasyMock根据swagger的定义自动生成mock项目。
  • 前后端分别开发,前端直接使用EasyMock的数据构建页面,后端进行真实接口的开发和测试。
  • 前后端完成开发之后,将前端从EasyMock切换到真实环境,进行联调。
  • 部署,测试,发布。

当然,每个公司,甚至每个项目组的情况都不一样,上面的应用流程不一定适用于所有的项目,如果大家在使用的过程中有发现任何问题或者疑问,或者有什么意见建议,欢迎随时与我们交流,共同成长。

附: 其他前端模拟工具

受篇幅限制,还有很多在前端模拟领域独当一面的优秀工具没有向大家详细介绍,如果EasyMock,以及上面提到的RAP和YApi都不能满足您的要求的话,您也可以试试以下这些:
NEI:网易的api接口管理平台,功能很完善,但是不开源。
apizza:在线模拟调试,快速生成api文档,导出离线版文档,功能完善,不开源。
AMP:github的mock开源项目。
Mockbin:可以从HAR文件生成一个模拟桩。
Doclever:编写REST接口文档,版本控制,生成测试数据。
mocky:无需登录,直接生成一个resposne,url不固定。
mock-api:一个网络服务,用来生成HTTP接口的模拟返回值。

 

文章来源:兴业数金研究开发中心 赵欢、潘英富 / 文

分享到: