Mock主要是用于前后端分离时,模拟交互时的返回数据
接下来介绍一下其它几种Mock的方式
json-server 与 express
之前介绍过json-server,可以启动一个express创建的mock的服务,通过接口获取数据;json-server也可以通过命令直接启动一个json文件作为mock服务,但是json-server直接启动json文件有一个无法避免的缺点:如果我们将mock数据放到一个json文件中,并且一个json文件中存在多个接口的数据,此时是以根节点下的字段属性作为接口路径的,但是接口路径可能是多个/
拼接而成,而字段属性是不支持设置复杂的路径的,所以使用起来没有那么方便
也介绍过express创建服务,直接返回数据,还介绍了express通过访问文件获取返回数据的方式
express与json-server的区别
-
MOCK服务的创建:
express直接创建mock服务;
json-server是可以使用其内置的express创建的mock服务,此方式可以使用多样性的接口路径;
json-server也可以使一个json文件成为mock服务,此方式接口路径受限
-
启动工具
通过node启动express创建mock的服务
node bin/index.js
json-server 直接启动.json文件
json-server db.json -p 3003
json-server 启动.js文件,文件中可以使用内置的express创建mock服务
json-server bin/index.js
express与json-server的相同点
两者启动js或mjs等可执行文件时,都可以在文件中通过对应的方法读取其它数据文件,也可以直接在执行文件中直接设置返回参数
express示例
import express from "express";
import cors from "cors";
import bodyParser from "body-parser";
import path from "path";
import { fileURLToPath } from 'url';//node内置模块
//es6中不可以直接使用__dirname
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const app = express(),API_PATH = "/api",port = 8888;
app.use(cors({ origin: "*" })); // 允许跨域
app.use(bodyParser.json()); // for parsing application/json
app.use(bodyParser.urlencoded({ extended: true })); // for parsing application/x-www-form-urlencoded
const router = express.Router();
//监听端口
app.listen(port, () => {//打印一下接口用例地址console.log(`测试读取数据地址:http://127.0.0.1:${port}${API_PATH}/`);
});//设置允许跨域访问
app.all("*", (req, res, next) => {const prefix="Access-Control-"res.header(prefix+"Allow-Origin", "*");res.header(prefix+"Allow-Headers", "*");res.header(prefix+"Allow-Methods", "*");// res.header("X-Powered-By", "http://www.shuzhiqiang.com");res.header("Content-Type", "application/json;charset=utf-8");res.header(prefix+"Allow-Credentials", true);//允许cookie跨域请求//防止在预请求阶段就响应接口req.method.toUpperCase() === "OPTIONS" ? res.sendStatus(200) : next();
});//添加接口 get、post
router.get('/search', function (req, res) {//获取请求的参数const param = req.body;//设置返回的数据res.json({mes: "info",resCode: 200,//1.直接设定任意返回值//2.读取文件获取到的处理值 均可data: {}});
});
//所有路由前添加API_PATH前缀
app.use(API_PATH, router);//增加数据
mockjs
mockjs是一个模拟返回数据的工具包
类型 | 方法 | 描述 |
---|---|---|
Boolean | Random.boolean | 可以生成基本数据类型 |
Natural | Random.natural(1, 100) | 生成1到100之间自然数 |
Integer | Random.integer(1, 100) | 生成1到100之间的整数 |
Float | Random.float(0, 100, 0, 5) | 生成0到100之间的浮点数,小数点后尾数为0到5位 |
Character | Random.character() | 生成随机字符串,可加参数定义规则 |
String | Random.string(2, 10) | 生成2到10个字符之间的字符串 |
Range | Random.range(0, 10, 2) | 生成一个随机数组 |
Date | Random.date() | 生成一个随机日期,可加参数定义日期格式 |
Image | Random.image(Random.size, ‘#02adea’, ‘Hello’) | Random.size表示将从size数据中任选一个数据 |
Color | Random.color() | 生成一个颜色随机值 |
Paragraph | Random.paragraph(2, 5) | 生成2至5个句子的文本 |
Name | Random.name() | 生成姓名 |
Url | Random.url() | 生成web地址 |
Address | Random.province() | 生成地址 |
具体的其它支持api可到官网查看
设定索引自动增长
attrName|number:[]表明数组中的内容重复的次数;
假设数组中是1个项目,那么Mock后最终返回的是number个元素;
假设数组中是2个项目,那么Mock后最终返回的是2*number个元素;
attrName|+number:每次自动增加number;
{status: 200,msg: "get_info success",'data|100': [//attrName|number{'index|+1': 1,fileName: "XXPAB00",ddType: 1,clientIP: "10.26.2.22",edate: 1579741,chId: "11",amount: 0.0}]
};
设定交互的类型: get || post
Mock.mock(RegExp("/assign" + ".*"), 'post', assign);
Mock.mock(RegExp("/group" + ".*"), "get", groups);
mockjs与express的结合应用
环境准备
npm i mockjs [-g]
npm i express [-g]
-g可选,表示全局安装,推荐项目级安装,方便以后的mock
创建mock.js
创建与src同级目录mock,创建mock.js文件
const express = require('express'); //引入express
const Mock = require('mockjs'); //引入mock
const app = express(); //实例化express
app.use('/login', function (req, res) {res.json(Mock.mock({'resCode|1': ['200', '100005', '100003'],'resMessage|1': ['角色精湛主题略荒诞', '疑信参半却无比期盼'],'data|1': [{'key|+1': 1,'maskedIdNo|1': ['123sss0', '12300', '12348900'],'maskedMobile|1': ['13820112810', '12345678900'],'personId|1': ['234788', '147258', '256987']}]}))
})
//IP :127.0.0.1 是本地IP地址
app.listen('8099', () => {console.log('监听端口 8099:http://127.0.0.1:8099/')
})
添加启动命令
添加"mock": “node mock/mock.js”
"scripts": {"start": "node scripts/start.js","build": "node scripts/build.js","test": "node scripts/test.js","mock": "node mock/mock.js"
}
调试
启动运行
刷新npm工具栏,双击mock
webstorm 与 vscode都支持npm脚本的界面化操作,如下图
webstorm:
vscode:
报错问题
因为启动的项目与启动的mock数据服务器端口号不同,因此存在跨域,导致报错
mock.js添加允许跨域内容
app.use(function(req, res, next) {res.header("Access-Control-Allow-Origin", "*");res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');res.header("Access-Control-Allow-Headers", "X-Requested-With");res.header('Access-Control-Allow-Headers', 'Content-Type');next();
});
重新启动,发现仍旧报错
Access to XMLHttpRequest at 'http://localhost:8099/login?idNo=35012&captcha=' from origin 'http://localhost:3000' has been blocked by CORS policy: The value of the 'Access-Control-Allow-Origin' header in the response must not be the wildcard '*' when the request's credentials mode is 'include'. The credentials mode of requests initiated by the XMLHttpRequest is controlled by the withCredentials attribute.
感觉是axios安全设置导致,发现存在设置
axios.defaults.withCredentials = true;// 表示跨域请求时是否需要使用凭证
注释该语句,刷新,重新请求,该问题解决
完整mock.js
let express = require('express'); //引入express
let Mock = require('mockjs'); //引入mock
let app = express(); //实例化expressapp.use(function(req, res, next) {res.header("Access-Control-Allow-Origin", "*");res.header('Access-Control-Allow-Methods', 'PUT, GET, POST, DELETE, OPTIONS');res.header("Access-Control-Allow-Headers", "X-Requested-With");res.header('Access-Control-Allow-Headers', 'Content-Type');next();
});app.use('/login',function(req, res){res.json(Mock.mock({'resCode|1': ['200','100005','100003'],'resMessage|1':['角色略荒诞', '理由人不安',],'data|1':[{'key|+1': 1,'maskedIdNo|1':['1234567890sss0','12345fff678900','12345888678900'],'maskedMobile|1': ['13820112810', '12345678900', '78945612301'],'personId|1': ['234788', '256987']}]}))
})
// http://127.0.0.1:8090/user.png
app.use(express.static("./assets"));//http://127.0.0.1:8090/assets/user.png
//注意,拼接路径,前段是"/assets",不是"assets",否则静态资源可能显示不出来
//可以访问assets文件夹下所有的静态资源
app.use("/assets", express.static("./assets"));//IP :127.0.0.1 是本地IP地址
app.listen('8099', () => {console.log('监听端口 8099:http://127.0.0.1:8099/')
})
直接使用mockjs
创建mock.js文件
import Mock from 'mockjs' // 引入mockjsconst Random = Mock.Random // Random 用于生成各种随机数据let data = [] // 用于接受生成数据的数组
let size = ['300x250', '250x250', '240x400', '336x280', '180x150', '720x300', '468x60', '234x60', '88x31', '120x90', '120x60', '120x240', '125x125', '728x90', '160x600', '120x600', '300x600'] // 定义随机值
for(let i = 0; i < 10; i ++) { // 可自定义生成的个数let template = {'Boolean': Random.boolean, // 可以生成基本数据类型'Natural': Random.natural(1, 10), // 生成1到10之间自然数'Integer': Random.integer(1, 100), // 生成1到100之间的整数'Float': Random.float(0, 100, 0, 5), // 生成0到100之间的浮点数,小数点后尾数为0到5位'Character': Random.character(), // 生成随机字符串,可加参数定义规则'String': Random.string(2, 10), // 生成2到10个字符之间的字符串'Range': Random.range(0, 10, 2), // 生成一个随机数组'Date': Random.date(), // 生成一个随机日期,可加参数定义日期格式'Image': Random.image(Random.size, '#02adea', 'Hello'), // Random.size表示将从size数据中任选一个数据'Color': Random.color(), // 生成一个颜色随机值'Paragraph':Random.paragraph(2, 5), //生成2至5个句子的文本'Name': Random.name(), // 生成姓名'Url': Random.url(), // 生成web地址'Address': Random.province() // 生成地址}data.push(template)
}
// 根据数据模板生成模拟数据
Mock.mock('/data/index', 'post', data)
存在问题
如果存在接口是另外一个接口的一部分,例如:
const api1=/dd/result;
const api2=/dd/result/history;
Mock.mock(RegExp(api1 + ".*"), "get", tpT);
Mock.mock(RegExp(api2 + ".*"), "get", detail);
调用接口api2时,因为两个Mock规则都符合要求,因此Mock文件会返回第一个符合要求的Mock结果,也就是如果是api1在前,api2在后,调用接口api2时,返回的是tpT,而应该返回的是detail!!!!!
那么如何解决该问题?!!在接口名称无法改变的情况下,这种问题可以通过调整Mock接口的顺序来解决,先拦截比较独特的接口:
Mock.mock(RegExp(api2 + ".*"), "get", detail);
Mock.mock(RegExp(api1 + ".*"), "get", tpT);
此时访问接口api2时,两个Mock规则都符合要求,但是返回第一个符合要求的Mock规则结果,访问接口api1,返回的是detail
使用方法
在启动的项目中直接引入 mock.js 的文件,不需要使用命令启动等就可以直接使用该mock了
例如,在main.ts等入口文件中引入该文件:
const MOCK_DATA = (flag) => {if (flag) {require('@/config/mock')}
}
可以根据条件决定是否引入mock文件,一旦被引入,发生接口调用的时候会直接返回MOCK数据,不引入mock文件的时候会调用真正的接口
直接使用与结合使用的区别
直接使用的方式决定了,该方式是当前项目级别的,其它项目不能使用该项目中的Mock;
而与express结合的方式,是单独启动的服务,当然也可以单独作为一个项目,因此,可以被其它项目调用的;
所以如果是不需要被公用的Mock,推荐直接使用,方便,并且不需要额外引入包;