问题描述:
开发过的项目老是打不开,因为离开公司后服务器用不了了。所以想着在公司开发的时候把数据都备份一下,供之后参考项目代码。
实现方法:
建一个Express服务,前端请求Express,Express代理目标服务器,通过请求api以json格式缓存到本地mocks文件夹内。第一次请求时缓存下来,第二次直接用缓存。
- 初始化项目
创建一个新目录并初始化 package.json:
bash
mkdir mock-server
cd mock-server
npm init -y
安装必要的依赖:
bash
npm install express axios fs path url crypto
- 创建代理服务器代码
创建 server.js 文件,内容如下:
const express = require('express');
const axios = require('axios');
const fs = require('fs');
const path = require('path');
const url = require('url');
const crypto = require('crypto');const app = express();
const MODE = 3; //1:根据路径生成缓存文件,2:根据路径和参数生成文件名,3:根据路径和参数和post 请求生成文件名
const PORT = 3000;
const TARGET_SERVER = 'http://192.168.0.184:6080'; // 替换为目标服务器地址const MOCK_DIR = path.join(__dirname, 'mocks');if (!fs.existsSync(MOCK_DIR)) {fs.mkdirSync(MOCK_DIR);
}app.use(express.json());// 工具函数:对对象进行排序序列化,用于生成稳定 hash
function sortedStringify(obj) {if (typeof obj !== 'object' || obj === null) return obj;// 如果是数组,则递归处理每一项if (Array.isArray(obj)) {return obj.map(item => sortedStringify(item));}const keys = Object.keys(obj).sort();const sorted = {};for (const k of keys) {sorted[k] = sortedStringify(obj[k]);}return JSON.stringify(sorted);
}// 工具函数:生成字符串的 hash,避免文件名过长
function getHash(str) {return crypto.createHash('md5').update(str).digest('hex');
}// 通用代理中间件
app.use(async (req, res) => {const parsedUrl = url.parse(req.url, true); // true 表示解析 query 参数const { pathname, query } = parsedUrl;// 构建缓存文件名:将 pathname 和 query 都作为标识const queryParamsStr = Object.entries(query).sort(([a], [b]) => a.localeCompare(b)) // 排序确保 key 顺序一致(避免缓存碎片).map(([k, v]) => `${k}=${v}`).join('&');let bodyHash = '';let fileNameBase = ``;let mockFilePath = ''if (MODE === 1) {mockFilePath = path.join(MOCK_DIR, `${pathname.replace(/\//g, '_')}.json`);} else if (MODE === 2) {fileNameBase = `${pathname.replace(/\//g, '_')}${queryParamsStr ? '_' + encodeURIComponent(queryParamsStr) : ''}`;mockFilePath = path.join(MOCK_DIR, `${fileNameBase}.json`);} else if (MODE === 3) {const queryStr = queryParamsStr ? '_' + encodeURIComponent(queryParamsStr) : '';if (req.method === 'POST' && req.body && Object.keys(req.body).length > 0) {const bodyStr = sortedStringify(req.body);bodyHash = '_' + getHash(bodyStr);// console.log(45, bodyHash, req.body)}fileNameBase = `${pathname.replace(/\//g, '_')}${queryStr}${bodyHash}`;mockFilePath = path.join(MOCK_DIR, `${fileNameBase}.json`);}// 检查本地是否存在缓存数据if (fs.existsSync(mockFilePath)) {const data = fs.readFileSync(mockFilePath, 'utf8');try {const jsonData = JSON.parse(data);console.log(`返回缓存数据: ${pathname}`);return res.json(jsonData);} catch (e) {console.error(`Failed to parse cached file: ${mockFilePath}`);}}// 否则转发请求到目标服务器try {const targetUrl = `${TARGET_SERVER}${parsedUrl.path}`;console.log('targetUrl:', targetUrl);const response = await axios({method: req.method,url: targetUrl,data: req.body,headers: req.headers,});// 将响应数据缓存到本地fs.writeFileSync(mockFilePath, JSON.stringify(response.data, null, 2), 'utf8');console.log(`缓存服务器数据: ${pathname}`);res.json(response.data);} catch (error) {console.error(`Proxy error for ${pathname}:`, error.message);res.status(error.response?.status || 500).json({error: error.message,});}
});app.listen(PORT, () => {console.log(`Mock server is running on http://localhost:${PORT}`);
});
注: 代码有3个模式,根据自己需要调。
1:根据路径生成缓存文件。如:get请求分页页面,pageNo传1和2只缓存1次。
2:根据路径和get的query参数生成缓存文件。如:get请求分页页面,pageNo传1和2缓存2次。
3:根据路径和参数和post 请求生成缓存文件(会缓存较多文件)。
- 使用方式
启动服务:
bash
node server.js
现在你本地运行了一个监听 http://localhost:3000 的代理服务器。
浏览器或前端请求示例:
将原本请求的目标 URL 改成:
http://localhost:3000/api/xxx
第一次请求会从真实服务器获取数据并缓存为文件(如:mocks/_api_xxx.json),后续请求直接使用本地缓存。
缓存文件: