从零开始搭建一个mock服务

栏目: Json · 发布时间: 6年前

内容简介:mock数据是一件很有意义的事情,前后端可以并行开发正是得益于mock生成的假数据,趁着有空撸了个轮子记录开发过程中的关注点:使用

mock数据是一件很有意义的事情,前后端可以并行开发正是得益于mock生成的假数据,趁着有空撸了个轮子 easy-config-mock ,以下记录实现思路以及最终实现的所有代码技术细节

关注点

记录开发过程中的关注点:

如何实现mock数据的功能(技术选型)

使用 mockjs 去生成假数据(附:mock规则)

使用 express 搭建服务(附:express官网)

如何集成到脚手架中,或者说工作流中

尽可能设计得简单,使其很容易集成到现有的脚手架或者工作流中,实际上 easy-config-mock 也做到了很容易集成,只需:

const easyConfigMock = require('easy-config-mock')

new easyConfigMock({
  // 将配置文件路径传递进去,服务会自动监听文件变化并重启服务
  path: path.resolve(__dirname, 'mock.config.js')
})

如何跟项目搭配使用

推荐将mock数据的配置文件放在项目的根目录下,原因在于mock数据跟业务是紧密联系的,丢在一起容易查阅与维护,如下:

+ vue-preject
-   node_modules
-   src
    mock.config.js   // mock数据的配置文件,名字仅做示例用

如何支持更复杂的场景

mockjs 的功能很强大,可以生成随机假数据,但在业务场景非常复杂的情况下这还不够,有时为了验证显示逻辑,期望是可以定制mock接口的返回

这是可以做到的,得益于express的中间件,看一下 easy-config-mock 中的配置文件是怎么写的, 更多说明

// mock.config.js
module.exports = {
  // common选项不是必须的,可以不用有该选项,内置的配置如下,当然你也可以更改
  common: {
    // mock服务的默认端口,如果端口被占用,会自动换一个
    port: 8018,
    // 如果你想看一下ajax的loading效果,该配置项可以设置接口的返回延迟
    timeout: 500,
    // 如果你想看一下接口请求失败的效果,将rate设置成0就可以了,rate取值范围0~1,代表成功的概率
    rate: 1,
    // 默认是true,自动开启mock服务,当然你也可以通过将其设置为false,关闭掉mock服务
    mock: true
  },
  // 普通的api...
  '/pkApi/getList': {
    code: 0,
    'data|5': [{
      'uid|1000-99999': 999,
      'name': '@cname'
    }],
    result: true
  },
  // 中间件api(标准的express中间件),这里你可以书写接口返回逻辑
  ['/pkApi/getOther'] (req, res, next) {
    const id = req.query.id
    req.myData = {   // 重要! 将返回数据挂载在req.myData
      0: {
        code: 0,
        'test|1-100': 100
      },
      1: {
        code: 1,
        'number|+1': 202
      },
      2:{
        code: 2,
        'name': '@cname'
      }
    }[id]
    next()  // 最后不要忘记手动调用一下next,不然接口就暂停处理了!
  }
}

easy-config-mock的优点

  • 很容易集成到脚手架或者工作流中,并且可以自动重启服务
  • 支持自定义中间件,以满足更为复杂的业务场景
  • 基本不会侵入业务代码,只需要将接口的请求前缀改成 http://127.0.0.1:mock端口
  • 配置文件丢在项目里面利于开发与维护

实现的具体技术细节

以下是实现该轮子需要的所有技术细节了,代码仅简要表达基本思想,详情内容请看 源码

如何监听 mock.config.js 文件变化

使用 chokidar 模块

const chokidar = require('chokidar')

chokidar.watch(somepath, {
  persistent: true
}).on('change', _ => {
  // file change...
  // do some logic...
})

如何实现服务的自动重启

fork 子进程去启动 express 服务,当配置文件发生变化的时候,杀掉子进程并重启服务

const childProcess = require('child_process')

let child
// 使用子进程启动express服务
child = childProcess.fork('./server.js', [], {
  encoding: 'utf8',
  execArgv: process.execArgv
})

chokidar.watch(somepath, {
  persistent: true
}).on('change', _ => {
  // 文件发生变化后杀死子进程并重启服务
  child.kill('SIGKILL')
  child = childProcess.fork('./server.js', [], {
    encoding: 'utf8',
    execArgv: process.execArgv
  })
})

子进程如何读取到配置文件数据

程序给父进程传递的数据子进程是不知道的,可以利用父子进程之间的通信,可以参考 child_process中子进程与父进程之间的通信与断开连接

// 给子进程传递数据
child.send({
  path: path
  ...
})
// 子进程接收到数据
process.on('message', ({ path, ... }) => {
  delete require.cache[path]
  // 这里,拿到了mock数据的配置项
  const options = require(path)
})

require是有缓存的,需要先删除require的缓存,再去重新获取配置文件的数据

如何去模拟jsonp的请求

首先得知道该请求是否是jsonp,检测请求链接是否带有callback参数

let dataType
app.use((req, res, next) => {
  dataType = req.query.callback ? 'jsonp' : 'json'
  next()
})

如何延迟接口的返回

有时,我们编写了loading的效果并想验证一下

const delayRes = (time) => (req, res, next) => {
  setTimeout(function() { next() }, time)
}
// 给接口增加1秒延迟
app.use(delayRes(1000))

如何让接口返回失败

有时,我们想看下断网或者服务器出错时的效果

const successRate = (rate) => (req, res, next) => {
  if (rate > Math.random()) return next()
  return next(500)
}
// 100%返回500错误
app.use(successRate(0))
app.use((err, req, res, next) => {
  res.status(500).json({ status: 0, code: 500, msg: 'Server Error' })
})

如何允许跨域

访问的非jsonp的mock接口是跨域请求 (协议,域名,端口三者相同才为同域) ,跨域请求是禁止的,会报错,这里需要设置为允许跨域

const crossDomain = () => (req, res, next) => {
  res.header("Access-Control-Allow-Origin", "*");
  res.header("Access-Control-Allow-Methods", "GET,HEAD,OPTIONS,POST,PUT");
  res.header("Access-Control-Allow-Headers", "Origin, X-Requested-With, Content-Type, Accept, Authorization");
  if (req.method === 'OPTIONS') res.status(200) // 让OPTIONS快速返回
  next();
}
app.use(crossDomain())

最终:路由创建

const { mock } = require('mockjs')

// options是配置文件里面的api信息
Object.keys(options).forEach(path => {
  const data = options[path]

  // 如果是自定义中间
  if (typeof data === 'function') app.use(data)

  app.use(path, (req, res, next) => {
    // req中带有myData的话说明是自定义中间件,否则是普通的mock api
    const rsp = req.myData ? mock(req.myData) : mock(data)
    res.status(200)[dataType](rsp)
  })
})

补充:easy-config-mock的webpack版插件

easy-mock-webpack-plugin

其实直接用 easy-config-mock 就可以了


以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

Once Upon an Algorithm

Once Upon an Algorithm

Martin Erwig / MIT Press / 2017-9-8 / GBP 22.95

How Hansel and Gretel, Sherlock Holmes, the movie Groundhog Day, Harry Potter, and other familiar stories illustrate the concepts of computing. Picture a computer scientist, staring at a screen and......一起来看看 《Once Upon an Algorithm》 这本书的介绍吧!

在线进制转换器
在线进制转换器

各进制数互转换器

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换