node实现mock-plugin中间件的方法

作者:185的胖哥 时间:2024-05-13 10:05:59 

写在前面

最近在使用Mockjs作为项目里面mock数据的工具,发现mockjs做的拦截部分是自己实现摸拟了一个XMLHttpRequest的方法做的拦截,使用Mockjs拦截请求后,在chromenetwork上无法看到请求(具体mockjs使用方法可以查看他的api,mockjs-api这里我不多做阐述),但为了更加真实的像后台返回数据,我自己使用Node作为中间代理去实现了一个mock-plugin.

express中间件介绍

因为插件相当于是实现了一个express的中间件的方式,所以这里简单对express中间件的使用做一个说明:

express中间件通过app.use(也有app.get,app.post等方法)的方式注册到express的实例某个属性上,将执行函数存放在栈内部,然后在回调执行的时候调用next()方法将执行下一个存在栈内的方法。

这里列举一个示例:


const express = require('express');
const app = express();

app.use(function (req, res, next) {
console.log('first all use');
next()
});

app.use(function (req, res, next){
setTimeout(() => {
 console.log(`two all use`)
 next()
}, 1000)
});

app.use(function (req, res, next) {
console.log('end all use')
next()
});

app.use('/', function (req, res, next) {
res.end('hello use')
});

app.listen(4000, function () {
console.log(`起动服务成功!`)
});

通过node执行以上代码后,在浏览器上通过访问http://locahost:4000可以看到控制台打印:

node实现mock-plugin中间件的方法

可以发现在执行的时候先执行了use注册的中间件,然后再执行到get路由的时候,又执行了app.use注册的中间件。

详细的express中间件可以在express官网查看

实现dev-server

devServer可以使用webpack-dev-server然后通过before的回调去做一层拦截,这样也能够实现在响应之前对后台的数据做一些处理。

我这儿选择自己实现一个devServer,在之前使用webpack-dev-server的服务大概需要配置,port, proxy,以及跨域https等。当然自己实现devServer就没必要实现那么多功能了,正常在开发场景下很多也不一定用得上,这里我主要使用了webpack-dev-middleware和webpack-hot-middleware达到自动编译和热更新的目的,以及可以自己在中间添加express中间件.

贴上代码:


onst path = require('path');
const express = require('express');
const webpack = require('webpack');
const webpackConfig = require('./webpack.dev');
const devMiddleware = require('webpack-dev-middleware');
const hotMiddleware = require('webpack-hot-middleware');
const app = express();
const compiler = webpack(webpackConfig); // webpack开发环境配置
const mockPlugin = require('./mock-plugin');

const config = {
prd: 8800
};

// 注册webpack-dev-middleware中间件
app.use(
devMiddleware(compiler, {
 publicPath: webpackConfig.output.publicPath
})
);

// 注册webpack-hot-middleware中间件
app.use(
hotMiddleware(compiler)
);

// 注册mockPlugin插件
app.use(
mockPlugin({
 routes: {
  '/app': 'http://locahost:3002', // 测试代理到服务器的地址
  '/api': 'http://localhost:3003' // 测试代理到服务器的地址
 },
 root: path.resolve(__dirname) // 项目根目录
})
);

app.listen(config.prd, function () {
console.log('访问地址:', `http://localhost:${config.prd}`);
});

具体的一些演示操作,这里也不多讲了(这不是实现mock-plugin的重点),网上也有很多如果通过webpack-dev-middlewarewebpack-hot-middleware的教程,唯一的区别是代理部分,网上可能用的是http-proxy之类已现有的工具,因为我们这儿需要在请求代理中间还需要处理一层,所以这儿我们自己实现mockPlugin注册进去。

摸拟mock文件

因为mock工具包含了一个请求后台的结果自动写入到Mock目录下。所以这里将目录层级设置为与请求路径保持一致:

api接口:/app/home/baseInfo => 目录:mock\app\home\baseInfo.js

对应 baseInfo.js 模板:


// mock 开关
exports.check = function () {
return true;
}
// mock 数据
exports.mockData = function () {
return {
 "success": true,
 "errorMsg": "",
 "data": {
  name: 'test'
 }
}
}

checktrue时对就请求将会取mockData的数据。

主逻辑实现

mock-plugin主要暴露一个高阶函数,第一层为请求代理配置,返回的函数的参数与app.get('/')的回调参数一致,不描述细节,大概输出主要的逻辑。


// 获取mock文件的mock数据
const setMockData = (moduleName) => {
const {mockData} = require(moduleName);

return mockData();
};

// 中间件暴露方法
module.exports = function (options) {
const {routes, root} = options;

return async (req, res, next) => {
 let {isReq, host} = await valid.isRequestPath(routes, req);

// 不是请求地址直接return掉
 if (!isReq) {
  next();
  return;
 }

// 如果存在Mock对应的文件
 let filePath = await valid.isMockFileName(root, req.path);

if (filePath) {
  // 检验本地mock文件开关是否开启
  let check = await valid.inspectMockCheck(filePath);
  if (check) {
   // 发送本地mock数据
   return res.send(setMockData(filePath))
  } else {
   // 请求结果
   let body = await request(host, req, res).catch(proxyRes => {
    res.status(proxyRes.statusCode);
   });
   // 发送请求的结果信息
   return res.send(body);
  }
 } else {
  // 请求返回主体
  let body = await request(host, req, res).catch(proxyRes => {
   res.status(proxyRes.statusCode);
   next();
  });

if (body) {
   // 定义需要写入文件路径
   const filePath = path.resolve(root, `mock${req.path}.js`);
   // 写入mock文件
   writeMockFile(filePath, body);
   // 响应返回主体
   return res.send(body);
  }
 }
};
};

以下是一些校验方法,详细代码就不贴了,具体源码可查看:https://github.com/moxaIce/lo...

  • isRequestPath校验是否为api接口请求, 返回 Promise包含isReq布尔值,host请求域名, route请求路由的对象

  • isMockFileName是否存在对应的mock文件,返回Promise返回匹配路径或者空字符串

  • inspectMockCheck校验模拟文件请求,开关是否开起, 返回布尔值

至于request方法和writeMockFile方法看下面的小结。

以下是我自己画的一个逻辑图,有点丑见谅:

node实现mock-plugin中间件的方法

请求代理

代理的作用不用多说,都知道是解决了前端起的服务和直接请求后台的跨域问题。我这儿主要是在中间件内部通过http.request方法发起一个http请求,对于http.request方法的使用可以看这里, 里面也有比较详细的示例,我这儿贴上我写的代码:


/**
* @description 请求方法
*/
const url = require('url');
const http = require('http');

module.exports = function (host, req, res) {
let body = '';

return new Promise((resolve, reject) => {
 const parse = url.parse(host);
 let proxy = http.request(
  {
   host: host.hostname,
   port: parse.port,
   method: req.method,
   path: req.path,
   headers: req.headers
  },
  (proxyRes) => {
   // 非200字段内直接响应错误 , 在主逻辑里处理
   if (proxyRes.statusCode < 200 || proxyRes.statusCode > 300) {
    reject(proxyRes)
   }

proxyRes.on('data', (chunk) => {
    body += chunk.toString();
   }).on('end', () => {
    try {
     resolve(JSON.parse(body));
    } catch (e) {
     // 将响应结果返回,在主文件做异常回调
     reject(proxyRes)
    }
   }).on('error', (err) => {
    console.log(`error is`, err);
   })
  });
 proxy.on('error', (e) => {
  console.error(`请求报错:${e.message}`)
 });
 proxy.end()
})
};

代理的实现比较简单,主要通过外层传入hostrequset, response在内部用url解析得到ip然后配置requestoptions, 通过监听dataend事件将得到的主体报文resolve出去,以及中间对非200段内的响应处理。

文件写入

通过中间传options传入的root, 可以得到完整的mock路径path.resolve(__dirname, mock${req.path}.js)。传入到写入mock文件方法里


module.exports = async function (filePath, body) {
await dirExists(path.dirname(filePath));

fs.writeFile(filePath, echoTpl(JSON.stringify(body)), function (err) {
 if (err) {
  console.log(`写入文件失败`)
 }
});
}

定义mockjs模板


module.exports = async function (filePath, body) {
await dirExists(path.dirname(filePath));

fs.writeFile(filePath, echoTpl(JSON.stringify(body)), function (err) {
 if (err) {
  console.log(`写入文件失败`)
 }
});
}

dirExists通过递归的方式写入文件目录


module.exports = async function (filePath, body) {
await dirExists(path.dirname(filePath));

fs.writeFile(filePath, echoTpl(JSON.stringify(body)), function (err) {
 if (err) {
  console.log(`写入文件失败`)
 }
});
}

效果演示

使用koa起一个node服务并且暴露如下路由和其中的数据,具体代码可以看这儿,我这儿只贴上了关键代码

服务端代码:


router.get('/app/home/baseInfo', user_controller.baseInfo)
router.post('/app/login', user_controller.login)

const login = async (ctx, next) => {
ctx.body = {
 success: true,
 message: '',
 code: 0,
 data: {
  a: 1,
  b: '2'
 }
}
};

const baseInfo = async (ctx, next) => {
ctx.body = {
 success: true,
 errorMsg: '',
 data: {
  avatar: 'http://aqvatarius.com/themes/taurus/html/img/example/user/dmitry_b.jpg',
  total: 333,
  completed: 30,
  money: '500'
 }
};
};

client代码


mounted() {
 axios.get('/app/home/baseInfo', function (res) {
  console.log(`res 23`, res)
 });

axios({
  url: '/app/login',
  method: 'post',
  headers: {
   // 'Content-Type': 'application/json;charset=UTF-8',
   'a': 'b'
  }
 })
}

具体效果可以看下图:

node实现mock-plugin中间件的方法

前端在访问的时候会将后台响应的数据自动写入到Mock目录下。

结语

感觉在写文章的过程中发现写入mock文件的时候可能使用stream会更好一点,年底了业务需求不太多,避免上班划水,随便想了写一写~ 觉得有用的可以点个收藏+赞~

来源:https://segmentfault.com/a/1190000021390640

标签:node,mock-plugin,中间件
0
投稿

猜你喜欢

  • windows下Anaconda的安装与配置正解(Anaconda入门教程) <font color=red>原创</font>

    2023-05-23 11:10:50
  • python GUI库图形界面开发之PyQt5美化窗体与控件(异形窗体)实例

    2023-07-19 00:34:06
  • PHP设计模式(八)装饰器模式Decorator实例详解【结构型】

    2023-11-24 05:59:31
  • python登录WeChat 实现自动回复实例详解

    2021-05-25 03:22:22
  • numpy模块中axis的理解与使用

    2023-06-26 22:55:28
  • python GUI库图形界面开发之PyQt5窗口布局控件QStackedWidget详细使用方法

    2023-11-13 13:35:51
  • python算法学习之桶排序算法实例(分块排序)

    2022-09-08 13:11:33
  • Vue 搭建Vuex环境详解

    2024-04-10 13:48:59
  • Mysql 远程连接配置实现的两种方法

    2024-01-22 10:31:23
  • pytorch+lstm实现的pos示例

    2023-08-11 22:02:10
  • pygame学习笔记(3):运动速率、时间、事件、文字

    2023-05-20 21:19:38
  • js中eval详解

    2024-04-19 10:01:30
  • Spring+MyBatis实现数据库读写分离方案

    2024-01-23 03:05:36
  • python实现图片素描效果

    2022-06-10 21:00:40
  • 最全的mysql查询语句整理

    2024-01-27 03:10:01
  • django model通过字典更新数据实例

    2021-08-15 13:00:15
  • setTimeout与setInterval的区别浅析

    2024-04-22 13:25:25
  • 页面嵌入Windows Media Player播放器代码需要注意的

    2023-07-02 17:04:48
  • JavaScript观察者模式(经典)

    2024-04-22 22:42:05
  • sql函数:去掉html代码

    2008-04-07 12:44:00
  • asp之家 网络编程 m.aspxhome.com