Open younth opened 7 years ago
fis3提供了强大的本地数据模拟的能力,与业务解耦的接口mock,在本地开发时候可谓非常方便。mock能力依赖基础的server.conf及数据文件,首先下面我们来看看基本用法。
server.conf
统一用mock目录作为数据模拟目录:
└── mock ├── sample.json └── server.conf
指令名称 正则规则 目标文件
指令名称
正则规则
目标文件
rewrite ^\/api\/user$ /mock/sample.json // sample.json内容 { "error": 0, "message": "ok", "data": { "username": "younth", "uid": 1, "age": 25, "company": "waimai" } }
node 服务器可以通过 js 的方式提供动态假数据。,动态数据本质是 express 的 route.
rewrite ^\/api\/dynamic\/time$ /mock/dynamic.js // dynamic.js内容 module.exports = function(req, res, next) { res.write('Hello world '); // set custom header. // res.setHeader('xxxx', 'xxx'); res.end('The time is ' + Date.now()); }; // 更复杂的,可以直接引用其他模块,发送请求 var http = require('http'); var url = require('url'); var util = require('util'); var querystring = require('querystring'); // 通过nodejs来抓取线上的结果。这样就完成了动态获取线上数据的功能 module.exports = function(request, response, next) { var method = request.method; ... };
// 支持正则分组,不支持post请求转发,后面有解决方案 proxy ^\/wmall\/privilege\/(.*)$ http://10.19.161.92:8059/wmall/privilege/$1
以上就是mock的基础用法。fis3的mock能力实现的并不复杂,依赖fis3-server-node模块。该模块的详细讲解放在后面。下面讲下post请求转发问题。
mock
fis3-server-node
post
proxy能力在联调阶段非常方便,但是目前fis3的proxy能力只能处理get请求。参见proxy bug issues。看起来可能是http-proxy模块的问题,但目前fis团队还未修复,下面提供两种方法办法。
http-proxy
rewrite ^\/wmall.*$ /mock/data.js
data.js在是一个expreses的路由,我们可以自己处理所有的请求。目前我自己项目中动态数据数据请求的代码,供参考:
data.js
// 注意这里面的.js,不是一般的.js 文件,而是相当于 express 的 route. var http = require('http'); var url = require('url'); var util = require('util'); var querystring = require('querystring'); var host = 'http://10.19.161.92:8059'; // 通过nodejs来抓取线上的结果。这样就完成了动态获取线上数据的功能 module.exports = function(request, response, next) { // // 最简单的测试 // response.writeHead(200, { // 'Content-type': 'text/html' // }); // response.end('Date.now() ' + Date.now()); // 获取 GET/POST var method = request.method; // 获取 post 时的参数 var postParams = request.body; // 解析参数 postParams = querystring.stringify(postParams) // 获取匹配到的 var originalUrl = request.originalUrl; // API 符合 /wmall... 格式 if (/^\/wmall.*$/.test(originalUrl) === false) { // API不是 /news?tn=... 格式,给出提示 response.writeHead(200, { 'Content-type': 'text/html' }); response.end('url format is not /wmall....'); return; } var apiUrl = host + originalUrl; var parsedUrl = url.parse(apiUrl, true); var options = { host: parsedUrl.hostname, port: parsedUrl.port || 80, path: parsedUrl.pathname, method: method, headers: {} }; // 携带cookie options.headers.cookie = request.headers.cookie; if (parsedUrl.search) { options.path += parsedUrl.search; } if (method.toLowerCase() === 'post') { // 增加 header参数 options.headers['Content-Type'] = 'application/x-www-form-urlencoded'; options.headers['Content-Length'] = postParams.length; } // 发送线上的数据请求,此时请求没有携带cookie var req = http.request(options, function (res) { res.setEncoding('utf8'); var str = ''; res.on('data', function (d) { // data 响应事件中先一步一步拼接数据 str += d; }); res.on('end', function () { // end 响应事件中,返回数据 var data; try { data = JSON.parse(str); } catch (ex) { data = str; } response.json(data); }); res.on('error',function (err) { // error 处理 response.end('reponse error: ' + err.message); }) }); req.on('error', function (err) { // error 处理 response.end('request error' + err.message); }); // 写入post参数 req.write(postParams); // 请求结束,告诉 response 可以返回了 req.end(); };
// fis3-server-node index.js var script = path.join(opt.root, 'server.js'); // www目录下不存在server.js 则走当前模块的app.js作为入口 if (!fis.util.exists(script)) { script = path.join(__dirname, 'app.js'); }
可以看出,server start的时候支持使用目录下的server.js, 在发布的目录下创建一个server.js, 就能自己实现server,使用http-proxy-middleware代理。我们完全可以用自己的server.js。
var express = require('express'); var path = require('path'); var url = require('url'); var fs = require('fs'); var bodyParser = require('body-parser') var proxy = require('http-proxy-middleware'); var args = process .argv .join('|'); var program = require('commander'); var port = /\-\-port\|(\d+)(?:\||$)/.test(args) ? ~~ RegExp.$1 : 8080; var https = /\-\-https\|(true)(?:\||$)/.test(args) ? !!RegExp.$1 : false; var DOCUMENT_ROOT = path.resolve(/\-\-root\|(.*?)(?:\||$)/.test(args) ? RegExp.$1 : process.cwd()); var app = express(); // logger app.use(require('morgan')('short')); console.log(args) // add proxy support var apiAddress = 'http://api.test.com'; app.use('/api', proxy({ target: apiAddress, changeOrigin: true, pathRewrite: { '^/api': '/nakedhub' }, })); // parse application/x-www-form-urlencoded app.use(bodyParser.urlencoded({extended: false})); // parse application/json app.use(bodyParser.json()); // 静态文件输出 app.use(express.static(DOCUMENT_ROOT, { index: [ 'index.html', 'index.htm', 'default.html', 'default.htm' ], extensions: ['html', 'htm'] })); // 静态文件列表。 app.use((function() { return function(req, res, next) { var pathname = url .parse(req.url) .pathname; var fullpath = path.join(DOCUMENT_ROOT, pathname); if (/\/$/.test(pathname) && fs.existsSync(fullpath)) { var stat = fs.statSync(fullpath); if (stat.isDirectory()) { var html = ''; var files = fs.readdirSync(fullpath); html = '<!doctype html>'; html += '<html>'; html += '<head>'; html += '<title>' + pathname + '</title>'; html += '</head>'; html += '<body>'; html += '<h1> - ' + pathname + '</h1>'; html += '<div id="file-list">'; html += '<ul>'; if (pathname != '/') { html += '<li><a href="' + pathname + '..">..</a></li>'; } files .forEach(function(item) { var s_url = path.join(pathname, item); html += '<li><a href="' + s_url + '">' + item + '</a></li>'; }); html += '</ul>'; html += '</div>'; html += '</body>'; html += '</html>'; res.send(html); return; } } next(); }; })()); // utf8 support app.use(function(req, res, next) { // attach utf-8 encoding header to text files. if (/\.(?:js|json|text|css)$/i.test(req.path)) { res.charset = 'utf-8'; } next(); }); // 错误捕获。 app.use(function(err, req, res, next) { console.log(err); }); // Bind to a port var server; if (https) { server = require('https').createServer({ key: fs.readFileSync(path.join(__dirname, 'key.pem'), 'utf8'), cert: fs.readFileSync(path.join(__dirname, 'cert.pem'), 'utf8') }, app); } else { server = require('http').createServer(app); } server .listen(port, '0.0.0.0', function() { console.log(' Listening on ' + (https ? 'https' : 'http') + '://127.0.0.1:%d', port); }); // 在接收到关闭信号的时候,关闭所有的 socket 连接。 (function() { var sockets = []; server.on('connection', function(socket) { sockets.push(socket); socket.on('close', function() { var idx = sockets.indexOf(socket); ~idx && sockets.splice(idx, 1); }); }); var finalize = function() { // Disconnect from cluster master process.disconnect && process.disconnect(); process.exit(0); } // 关掉服务。 process.on('SIGTERM', function() { console.log(' Recive quit signal in worker %s.', process.pid); sockets.length ? sockets.forEach(function(socket) { socket.destroy(); finalize(); }) : server.close(finalize); }); })(server);
fis3/node_modules/fis3-server-node
mark
fis3提供了强大的本地数据模拟的能力,与业务解耦的接口mock,在本地开发时候可谓非常方便。mock能力依赖基础的
server.conf
及数据文件,首先下面我们来看看基本用法。目录结构
统一用mock目录作为数据模拟目录:
server.conf 配置语法
指令名称
支持 rewrite 、 redirect 和 proxy。正则规则
用来命中需要作假的请求路径目标文件
设置转发的目标地址,需要配置一个可请求的 url 地址。mock 静态假数据
mock 动态假数据
proxy 到其他服务的 api 地址
以上就是
mock
的基础用法。fis3的mock
能力实现的并不复杂,依赖fis3-server-node
模块。该模块的详细讲解放在后面。下面讲下post
请求转发问题。如何proxy 在处理post请求时候的bug
proxy能力在联调阶段非常方便,但是目前fis3的proxy能力只能处理get请求。参见proxy bug issues。看起来可能是
http-proxy
模块的问题,但目前fis团队还未修复,下面提供两种方法办法。1. 利用动态数据能力
data.js
在是一个expreses的路由,我们可以自己处理所有的请求。目前我自己项目中动态数据数据请求的代码,供参考:利用fis server配置文件
可以看出,server start的时候支持使用目录下的server.js, 在发布的目录下创建一个server.js, 就能自己实现server,使用http-proxy-middleware代理。我们完全可以用自己的server.js。
fis3 mock server 源码解读