写在前面
最近在使用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;charset=UTF-8', 'a': 'b' } }) }
具体效果可以看下图:
前端在访问的时候会将后台响应的数据自动写入到Mock目录下。
结语
感觉在写文章的过程中发现写入mock
文件的时候可能使用stream
会更好一点,年底了业务需求不太多,避免上班划水,随便想了写一写~ 觉得有用的可以点个收藏+赞~
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持。
免责声明:本站资源来自互联网收集,仅供用于学习和交流,请遵循相关法律法规,本站一切资源不代表本站立场,如有侵权、后门、不妥请联系本站删除!
《魔兽世界》大逃杀!60人新游玩模式《强袭风暴》3月21日上线
暴雪近日发布了《魔兽世界》10.2.6 更新内容,新游玩模式《强袭风暴》即将于3月21 日在亚服上线,届时玩家将前往阿拉希高地展开一场 60 人大逃杀对战。
艾泽拉斯的冒险者已经征服了艾泽拉斯的大地及遥远的彼岸。他们在对抗世界上最致命的敌人时展现出过人的手腕,并且成功阻止终结宇宙等级的威胁。当他们在为即将于《魔兽世界》资料片《地心之战》中来袭的萨拉塔斯势力做战斗准备时,他们还需要在熟悉的阿拉希高地面对一个全新的敌人──那就是彼此。在《巨龙崛起》10.2.6 更新的《强袭风暴》中,玩家将会进入一个全新的海盗主题大逃杀式限时活动,其中包含极高的风险和史诗级的奖励。
《强袭风暴》不是普通的战场,作为一个独立于主游戏之外的活动,玩家可以用大逃杀的风格来体验《魔兽世界》,不分职业、不分装备(除了你在赛局中捡到的),光是技巧和战略的强弱之分就能决定出谁才是能坚持到最后的赢家。本次活动将会开放单人和双人模式,玩家在加入海盗主题的预赛大厅区域前,可以从强袭风暴角色画面新增好友。游玩游戏将可以累计名望轨迹,《巨龙崛起》和《魔兽世界:巫妖王之怒 经典版》的玩家都可以获得奖励。
更新日志
- 凤飞飞《我们的主题曲》飞跃制作[正版原抓WAV+CUE]
- 刘嘉亮《亮情歌2》[WAV+CUE][1G]
- 红馆40·谭咏麟《歌者恋歌浓情30年演唱会》3CD[低速原抓WAV+CUE][1.8G]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[320K/MP3][193.25MB]
- 【轻音乐】曼托凡尼乐团《精选辑》2CD.1998[FLAC+CUE整轨]
- 邝美云《心中有爱》1989年香港DMIJP版1MTO东芝首版[WAV+CUE]
- 群星《情叹-发烧女声DSD》天籁女声发烧碟[WAV+CUE]
- 刘纬武《睡眠宝宝竖琴童谣 吉卜力工作室 白噪音安抚》[FLAC/分轨][748.03MB]
- 理想混蛋《Origin Sessions》[320K/MP3][37.47MB]
- 公馆青少年《我其实一点都不酷》[320K/MP3][78.78MB]
- 群星《情叹-发烧男声DSD》最值得珍藏的完美男声[WAV+CUE]
- 群星《国韵飘香·贵妃醉酒HQCD黑胶王》2CD[WAV]
- 卫兰《DAUGHTER》【低速原抓WAV+CUE】
- 公馆青少年《我其实一点都不酷》[FLAC/分轨][398.22MB]
- ZWEI《迟暮的花 (Explicit)》[320K/MP3][57.16MB]