侯体宗的博客
  • 首页
  • Hyperf版
  • beego仿版
  • 人生(杂谈)
  • 技术
  • 关于我
  • 更多分类
    • 文件下载
    • 文字修仙
    • 中国象棋ai
    • 群聊
    • 九宫格抽奖
    • 拼图
    • 消消乐
    • 相册

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

前端  /  管理员 发布于 4年前   317

写在前面

最近在使用Mockjs作为项目里面mock数据的工具,发现mockjs做的拦截部分是自己实现摸拟了一个XMLHttpRequest的方法做的拦截,使用Mockjs拦截请求后,在chrome的network上无法看到请求(具体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可以看到控制台打印:

可以发现在执行的时候先执行了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-middleware和webpack-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'  } }}

当check为true时对就请求将会取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方法看下面的小结。

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

请求代理

代理的作用不用多说,都知道是解决了前端起的服务和直接请求后台的跨域问题。我这儿主要是在中间件内部通过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() })};

代理的实现比较简单,主要通过外层传入host和requset, response在内部用url解析得到ip然后配置request的options, 通过监听data与end事件将得到的主体报文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;',    'a': 'b'   }  }) }

具体效果可以看下图:


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

结语

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

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

您可能感兴趣的文章:

  • 浅谈Node.js 中间件模式
  • node.js中路由,中间件,ge请求和post请求的参数详解
  • node中koa中间件机制详解
  • node.js 中间件express-session使用详解
  • 深入理解nodejs中Express的中间件
  • nodejs处理图片的中间件node-images详解
  • nodejs开发――express路由与中间件
  • node.js cookie-parser 中间件介绍
  • nodejs实现黑名单中间件设计


  • 上一条:
    Laravel接收前端ajax传来的数据的实例代码
    下一条:
    JS window对象简单操作完整示例
  • 昵称:

    邮箱:

    0条评论 (评论内容有缓存机制,请悉知!)
    最新最热
    • 分类目录
    • 人生(杂谈)
    • 技术
    • linux
    • Java
    • php
    • 框架(架构)
    • 前端
    • ThinkPHP
    • 数据库
    • 微信(小程序)
    • Laravel
    • Redis
    • Docker
    • Go
    • swoole
    • Windows
    • Python
    • 苹果(mac/ios)
    • 相关文章
    • 使用 Alpine.js 排序插件对元素进行排序(0个评论)
    • 在js中使用jszip + file-saver实现批量下载OSS文件功能示例(0个评论)
    • 在vue中实现父页面按钮显示子组件中的el-dialog效果(0个评论)
    • 使用mock-server实现模拟接口对接流程步骤(0个评论)
    • vue项目打包程序实现把项目打包成一个exe可执行程序(0个评论)
    • 近期文章
    • 在go语言中使用api.geonames.org接口实现根据国际邮政编码获取地址信息功能(1个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf分页文件功能(0个评论)
    • gmail发邮件报错:534 5.7.9 Application-specific password required...解决方案(0个评论)
    • 欧盟关于强迫劳动的规定的官方举报渠道及官方举报网站(0个评论)
    • 在go语言中使用github.com/signintech/gopdf实现生成pdf文件功能(0个评论)
    • Laravel从Accel获得5700万美元A轮融资(0个评论)
    • 在go + gin中gorm实现指定搜索/区间搜索分页列表功能接口实例(0个评论)
    • 在go语言中实现IP/CIDR的ip和netmask互转及IP段形式互转及ip是否存在IP/CIDR(0个评论)
    • PHP 8.4 Alpha 1现已发布!(0个评论)
    • Laravel 11.15版本发布 - Eloquent Builder中添加的泛型(0个评论)
    • 近期评论
    • 122 在

      学历:一种延缓就业设计,生活需求下的权衡之选中评论 工作几年后,报名考研了,到现在还没认真学习备考,迷茫中。作为一名北漂互联网打工人..
    • 123 在

      Clash for Windows作者删库跑路了,github已404中评论 按理说只要你在国内,所有的流量进出都在监控范围内,不管你怎么隐藏也没用,想搞你分..
    • 原梓番博客 在

      在Laravel框架中使用模型Model分表最简单的方法中评论 好久好久都没看友情链接申请了,今天刚看,已经添加。..
    • 博主 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 @1111老铁这个不行了,可以看看近期评论的其他文章..
    • 1111 在

      佛跳墙vpn软件不会用?上不了网?佛跳墙vpn常见问题以及解决办法中评论 网站不能打开,博主百忙中能否发个APP下载链接,佛跳墙或极光..
    • 2016-10
    • 2016-11
    • 2017-06
    • 2017-07
    • 2017-08
    • 2017-09
    • 2017-10
    • 2017-11
    • 2018-03
    • 2018-04
    • 2018-05
    • 2018-06
    • 2018-09
    • 2018-11
    • 2018-12
    • 2019-02
    • 2020-03
    • 2020-04
    • 2020-05
    • 2020-06
    • 2021-04
    • 2021-05
    • 2021-07
    • 2021-08
    • 2021-09
    • 2021-10
    • 2021-11
    • 2022-08
    • 2022-09
    • 2022-10
    • 2022-11
    • 2022-12
    • 2023-01
    • 2023-02
    • 2023-03
    • 2023-04
    • 2023-05
    • 2023-06
    • 2023-07
    • 2023-09
    • 2023-10
    • 2023-11
    • 2023-12
    • 2024-01
    • 2024-02
    • 2024-03
    • 2024-04
    Top

    Copyright·© 2019 侯体宗版权所有· 粤ICP备20027696号 PHP交流群

    侯体宗的博客