木子手记

用Nodejs写一个Mock Server

金金 ( 更新) NodejsMock Server

一、事情的缘由

最近我们的一个项目,在开发的时候,遇到一个蛋疼的问题。由于需要依赖后端开发提供接口及数据,所以我们都开发机上部署代码,但是有很多个同事同时开发,经常发生代码覆盖的情况。 为了解决这个破问题,我们希望能够在本地进行开发及调试,这时又存在两个蛋疼的问题:

  1. 接口尚未开发完成
  2. 接口跨域,不能访问

对于这两个问题,其实也好解决,通过 fiddler 或者 nginx 都可以轻易解决。不过这些方案都不能通用,每个开发都需要自己单独配置,通用性和复用性不强,我们需要更加通用的解决方案。 最后,觉得我们还是需要一个自己的专用 Mock Server

二、Mock Server

1. 什么是Mock Server?

为了更好的分工合作,让前端能在不依赖后端环境的情况下进行开发,其中一种手段就是为前端开发者提供一个web容器,这个本地环境就是 mock server

2. Mock Server需要提供哪些能力?

想要前端不依赖后端的环境下正常运行,Mock Server需要下列能力:

  • 能渲染模板
  • 实现请求路由映射
  • 数据接口代理到生产或者测试环境

Mock Server的运行机理: MockServer

由于我们这只是一个纯前端项目,所以我们不需要 渲染模板 的功能。我们只需要一个能够 路由转发数据接口 代理的Server。

三、用NodeJs实现一个Mock Server

1. 启动一个Server

通过 http 模块创建一个server。

http.createServer(function(req, res) {
    var urlObj = url.parse(req.url.replace('/static/hybrid', ''));  //修正fis release 添加的文件路径
    var pname = urlObj.pathname;
    // 静态资源
    if (pname == '/' || /\.\w{2,5}$/.test(pname)) {
        findStaticResource(pname, req, res);
    } else {
        // 判断是json, 还是jsonp
        var contentType = 'application/json;charset=UTF-8';
        var search = urlObj.search;
        var cb = '';

        if (search && /[?&](?:callback|cb)=([^&]+)/i.test(search)) {
            contentType = 'application/x-javascript';
            cb = RegExp.$1;
        }

        // 重写
        if (pname in rewriteList) {
            var localPath = path.normalize(config.cwd + '/test' + rewriteList[pname]);
            rewriteResource(req, res, localPath, contentType, cb);
        } else { // 转发
            redirectResource(req, res, localPath, contentType, pname);
        }
    }
}).listen(config.port);

2. 读取静态文件(html、css、js)

静态资源处理起来还是很简单的。

// 处理静态资源
function loadStaticResource(pathname, req, res) {
    var ext = path.extname(pathname);
    fs.readFile(pathname, "binary", function(error, file) {
        if (error) {
            res.writeHead(500, {
                "Content-Type": "text/plain"
            });
            res.end("Server Error:" + error);
        } else {
            res.writeHead(200, {
                "Content-Type": getContentTypeByExt(ext)
            });
            res.end(file, "binary");
        }
    });
}

3. 代理数据接口

数据代理,接口重定向,最主要是依赖 http.request 方法。

// 重定向
function redirectResource(req, res, localPath, contentType, pname) {
    var host = redirectList['host'] || '', u = {};
    if (host) { //通用配置
        u = url.parse(host);
    }
    if (pname in redirectList) { // 特定url配置
        u = url.parse(redirectList[pname].host);
    };
    var options = {
        host: u.hostname || config.default.hostname,
        port: u.port || config.default.port,
        path: req.url,
        method: req.method,
    };
    var xreq = http.request(options, function(_res, _req) {
        var body = '';
        _res.on('data', function(d) {
            body += d;
        }).on('end', function() {
            res.writeHead(200, {
                "Content-Type": contentType
            });
            res.end(body, "binary");
        });
    }).on('error', function(e) {
        console.log("Got error: " + e.message);
    });
    xreq.end();
}

四、总结

这个 mock server 功能相对比较简单,主要的工作在于思路。首先必须要能够和我们的开发构建工具 fis 适配,其次在于路由的转发规则及转发配置文件。

部分资料参考: 知乎:你是如何构建 Web 前端 Mock Server 的?

金金
一枚奔跑在前端路上的男子