問題描述:
開發過的項目老是打不開,因為離開公司后服務器用不了了。所以想著在公司開發的時候把數據都備份一下,供之后參考項目代碼。
實現方法:
建一個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),后續請求直接使用本地緩存。
緩存文件:
?