实用!前后端分离开发之前端模拟数据

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.js网址

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地址

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文档,导出离线版文档,功能完善,不过没有开源。

了解更多:https://app.apiary.io/

6.小结

在前后端分离的架构中,为了让前端同学在前后端联调时摆脱“等接口-改接口-调接口”的尴尬境地,减少双方的沟通成本、时间成本,提高项目整体开发效率,缩短开发周期。前端同学可以模拟数据,具体可以前端本地模拟数据,或者使用一些前端接口模拟工具。至于哪种方法更好,还需要视具体情况而定。比如一个比较小的项目,前端本地模拟数据足矣,如果为了一个小项目,还需要本地搭建一套接口模拟工具,倒有些“杀鸡用牛刀”了。不过长远而看,使用前端接口模拟工具会带来很多的便利,值得我们花时间去了解和使用。

原创声明,本文系作者授权云+社区发表,未经许可,不得转载。

如有侵权,请联系 yunjia_community@tencent.com 删除。

编辑于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏暴走大数据

Spark Core源码精读计划5 | 事件总线及ListenerBus

在讲解SparkContext组件初始化时,第一个初始化的内部组件就是LiveListenerBus,后面的组件很多都会依赖它,这从侧面说明事件总线是非常重要的...

14130
来自专栏全栈者

[每日一题] JavaScript面试之“大数相加”运算

为什么会出现这个原因呢?先来探究一下Javascript的Number类型本质了,先来看看最权威的MDN对Javascript数字类型的定义。

13520
来自专栏程序员成长指北

深入理解Node.js 进程与线程(8000长文彻底搞懂)

进程与 线程是一个程序员的必知概念,面试经常被问及,但是一些文章内容只是讲讲理论知识,可能一些小伙伴并没有真的理解,在实际开发中应用也比较少。本篇文章除了介绍概...

19610
来自专栏深度学习与python

快速学习正则表达式的中文资源网站

无论你使用的是Python、Java、Perl还是Shell,正则表达式是学习主流编程语言几乎绕不开的话题。有了它,就能帮你快速定位到符合筛选条件的文本内容。

11220
来自专栏暴走大数据

Spark Core源码精读计划6 | AsyncEventQueue与LiveListenerBus

在上一篇文章中,我们了解了Spark事件总线机制的概况,以及ListenerBus、SparkListenerBus的细节。

12030
来自专栏Vi的技术博客

Java Grammar:数据类型

我们知道,Java是一种 强类型 语言,类型对于Java语言来说非常的重要不言而喻,在Java中,分为 基础数据类型 和 引用数据类型 ,其中基础数据类型分为了...

8620
来自专栏全栈者

【精品转载】学习 Vue 源码的必要知识储备

我最近在写 Vue 进阶的内容。在这个过程中,有些人问我看 Vue 源码需要有哪些准备吗?所以也就有了这篇计划之外的文章。

13930
来自专栏全栈者

一步一步解析Axios源码,从入门到原理

一个基于 Promise 来管理 http 请求的简洁、易用且高效的代码封装库。通俗一点来讲,它是一个前端替代Ajax的一个东西,可以使用它发起http请求接口...

63610
来自专栏痴者工良

ASP.NET Core 编码、web编码、网页编码 System.Text.Encodings.Web

空间包含表示 Web 编码器的基类、表示 HTML、JavaScript 和 Url 字符编码的子类,以及表示仅允许编码特定字符、字符范围或码位的筛选器的类。

13450
来自专栏量子位

百度无人驾驶出租长沙开测:Apollo技术一汽红旗车队,年内服役

8月2日,百度与中国一汽红旗共同打造的国内首批量产L4级自动驾驶出租车Robotaxi-红旗E·界亮相长沙展开测试。

8220

扫码关注云+社区

领取腾讯云代金券

年度创作总结 领取年终奖励