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


Posted in Javascript onDecember 25, 2019

写在前面

最近在使用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会更好一点,年底了业务需求不太多,避免上班划水,随便想了写一写~ 觉得有用的可以点个收藏+赞~

以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持三水点靠木。

Javascript 相关文章推荐
IE Firefox 使用自定义标签的区别
Oct 15 Javascript
Javascript实现的鼠标经过时播放声音
May 18 Javascript
jquery插件制作教程 txtHover
Aug 17 Javascript
js获取php变量的实现代码
Aug 10 Javascript
利用JavaScript检测CPU使用率自己写的
Mar 22 Javascript
分享9点个人认为比较重要的javascript 编程技巧
Apr 27 Javascript
Flow之一个新的Javascript静态类型检查器
Dec 21 Javascript
实例剖析AngularJS框架中数据的双向绑定运用
Mar 04 Javascript
浅析node.js的模块加载机制
May 25 Javascript
详解Vue CLI3配置解析之css.extract
Sep 14 Javascript
如何优雅地在vue中添加权限控制示例详解
Mar 07 Javascript
vue router 传参获取不到的解决方式
Nov 13 Javascript
微信小程序停止其他视频播放当前视频的实例代码
Dec 25 #Javascript
vue分页插件的使用方法
Dec 25 #Javascript
继承行为在 ES5 与 ES6 中的区别详解
Dec 24 #Javascript
在JavaScript中实现链式调用的实现
Dec 24 #Javascript
vue实现分页加载效果
Dec 24 #Javascript
微信小程序如何获取地址
Dec 24 #Javascript
浅析vue-router中params和query的区别
Dec 24 #Javascript
You might like
PHP中根据IP地址判断城市实现城市切换或跳转代码
2012/09/04 PHP
PHP中常用的输出函数总结
2014/09/22 PHP
php使用unset()删除数组中某个单元(键)的方法
2015/02/17 PHP
php+ajax实现无刷新的新闻留言系统
2020/12/21 PHP
Laravel如何同时连接多个数据库详解
2019/08/13 PHP
Laravel实现搜索的时候分页并携带参数
2019/10/15 PHP
详解phpstorm2020最新破解方法
2020/09/17 PHP
javascript让setInteval里的函数参数中的this指向特定的对象
2010/01/31 Javascript
jQuery模拟超链接点击效果代码
2013/04/21 Javascript
js验证整数加保留小数点的简单实例
2013/12/02 Javascript
JS常用字符串处理方法应用总结
2014/05/22 Javascript
node.js中的fs.fstat方法使用说明
2014/12/15 Javascript
在Node.js中使用Javascript Generators详解
2016/05/05 Javascript
js和C# 时间日期格式转换的简单实例
2016/05/28 Javascript
js中遍历Map对象的方法
2016/07/27 Javascript
JavaScript实现的贝塞尔曲线算法简单示例
2018/01/30 Javascript
手把手教你使用TypeScript开发Node.js应用
2019/05/06 Javascript
JavaScript闭包相关知识解析
2019/10/19 Javascript
vue实现导航标题栏随页面滚动渐隐渐显效果
2020/03/12 Javascript
JS实现滑动拼图验证功能完整示例
2020/03/29 Javascript
Python的Django框架中的select_related函数对QuerySet 查询的优化
2015/04/01 Python
Python解析json时提示“string indices must be integers”问题解决方法
2019/07/31 Python
学python安装的软件总结
2019/10/12 Python
pandas实现导出数据的四种方式
2020/12/13 Python
武汉世纪畅想数字传播有限公司.NET笔试题
2014/07/22 面试题
国贸专业大学生职业生涯规划范文
2014/01/10 职场文书
信息技术教学反思
2014/02/12 职场文书
学校领导班子群众路线整改措施
2014/09/16 职场文书
无刑事犯罪记录证明
2014/09/18 职场文书
个人师德师风自我剖析材料
2014/09/29 职场文书
结婚老公保证书
2015/02/26 职场文书
2015年出纳工作总结与计划
2015/05/18 职场文书
大学生暑期实践报告
2015/07/13 职场文书
Pandas实现DataFrame的简单运算、统计与排序
2022/03/31 Python
Django框架中表单的用法
2022/06/10 Python
SQL bool盲注和时间盲注详解
2022/07/23 SQL Server