一、前言
开发了个Mock CLI工具,助力开发速度提升一倍,欢迎使用和点赞:
支持能力
- Mock API
- 读取Swagger文档,并生成API接口
- 无需担心跨域问题,在反代理时自行在本地配置
- 安装方便:node环境一行指令安装/也支持Docker启动
- API生成读取顺序:本地mock -> swagger -> remote api
在开发过程中,也遇到了一些难点,做一下复盘
- ESM引入和Require引入的差异导致一些写法的不同
- 关于API路径匹配问题
- Swagger版本不同,json文件字段的不同
- 通过Github构建不同的Docker镜像,发送Docker hub
- 通过配置代理和中间件来转发不同的请求
二、实现原理图
1、原理图
2、原理描述
- 基于
Express框架构建当前的API处理,通过http-proxy-middleware实现请求的代理 - 当前用户发起请求后,通过中间件对路由进行过滤,然后根据配置代理优先级匹配路由,默认先匹配Local JSON,接着是Swagger JSON,最后代理到Proxy API
- 通过Mockjs对不同的字段类型进行生成,然后返回
三、项目实战
1、配置命令行参数
首先我们配置mock命令,设定参数
type: 分别代表API类型,这里写一下Restful风格的实现思路
port: 服务启动的端口
配置package.json
{
"main": "index.js",
"files": [
"bin",
"command",
"public",
"utils",
"index.js",
"package.json",
"server.js",
"README.md"
],
"type": "module",
"bin": {
"dev-mock-cli": "./bin/index.js"
}
}
入口文件index.js
必须添加 #!/usr/bin/env node,否则无法执行
#!/usr/bin/env node
class MockServer {
constructor(config) {
this.config = config;
// 初始化配置
}
// 启动服务器
async start() {
// 判断当前要使用的端口是否被占用
const newPort = await getIdlePort(this.port);
// 启动服务器
const app = express();
app.use(express.urlencoded({ extended: true }));
app.use(express.json({ limit: '50mb' }));
// 通用中间件
app.all('*', cors);
app.all('*', timeoutSetting);
// 根据类型加载模块
const loadModule = this.type === 'action' ? action : restful;
loadModule({ app, filePath: this.filePath, config: this.config });
this.startServer(app);
}
// 启动Express服务器并打印启动日志
startServer(app) {
app.listen(this.port, () => logServerStart(this.port, this.type));
}
}
export default MockServer;
上面的做了三件事:
- 1、初始化数据,读取mock.config.json
- 2、启动
Expresss - 3、根据要支持的API风格,加载不同的逻辑
2、加载restful风格API逻辑
const restful = async ({ app, filePath, config }) => {
const { proxyApiUrl, swaggerApi=[] } = config;
// 如果没有文件,新建example
checkFileExist(filePath, true)
const apiList = getAllAPIPath(filePath);
// 获取本地所有的path,生成本地代理
app.all(apiList, async (req, res) => {
const url = req.path;
checkFileExistsAndRespond(url, filePath, req, res);
});
await fetchAndCreateRoutes({app, swaggerApi});
// 没有本地mock,则读取远程接口
if (proxyApiUrl) {
const proxyMiddleware = createProxyMiddleware({
target: proxyApiUrl,
changeOrigin: true,
})
app.use('*', proxyMiddleware);
}
};
export default restful;
上面实现了几个方法
- 为了方便CLI快速上手,我们直接创建了mock文件夹和例子。
- 为了适配到swagger和远程的API,我们这里将本地的json文件进行聚合,优先实现本地路由转发
- 然后是根据mock.config.json中的的Swagger加载json文件,根据每个字段的类型进行解析出api请求
- 最后如果配置了proxyApiUrl字段,会将前面没有匹配到的路由,代理到proxyApiUrl的路由
3、检查、生成Mock数据
原理:我们以目录为api请求路径,以文件名为请求方法。
不存在路由参数的请求:
- 比如
/v1/list/get.json,当前文件匹配的请求为Get方法,路径为/v1/list - 比如
/v1/list/post.json,当前文件匹配的请求为Post方法,路径为/v1/list
存在路由参数的请求:
- 比如
/v1/list/{id}/get.json, 当前文件匹配的请求为Get请求,id可以被替换成任意数字或字符串,匹配路径有:/v1/list/1/v1/list/abc
4、根据swagger json数据生成API返回数据
// 生成 Mock 值的示例函数
const generateMockValue = (schema, openAPIDoc) => {
if (schema.$ref) {
// 解析 $ref 并递归处理
const resolvedSchema = resolveRef(schema.$ref, openAPIDoc);
return generateMockValue(resolvedSchema, openAPIDoc);
}
switch (schema.type) {
case 'string':
return Mock.mock('@string(5, 10)');
case 'number':
return Mock.mock('@integer(0, 1)');
case 'integer':
return Mock.mock('@integer(0, 1)');
case 'boolean':
return true;
case 'array':
return [generateMockValue(schema.items, openAPIDoc)];
case 'object':
const mockObject = {};
for (const [key, value] of Object.entries(schema.properties)) {
mockObject[key] = generateMockValue(value, openAPIDoc);
}
return mockObject;
default:
return null;
}
};
这里我们可以根据谁类型生成对应的mock数据,也可以通过配置文件来自定义生成规则。要注意的是swagger2.x和3.x的字段有些不同,需要做好不同情况的适配。
5、自动构建Docker镜像
通过Github构建不同的Docker镜像,发送Docker hub。
完成后发现启动这个API服务后,可以通过docker处理,这样也方便随时使用,镜像适配的平台不同,我们通过docker/build-push-action@v5,来配置platforms实现多个平台镜像的构建,然后通过Action推送到Docker hub
# 构建多平台镜像
- name: Build and push Docker image
uses: docker/build-push-action@v5
with:
context: .
platforms: linux/amd64,linux/arm64
push: true
tags: |
${{ secrets.DOCKER_USERNAME }}/dev-mock-cli:latest
四、总结
对于大部分mock来说,复杂的配置是不需要的,一个简单的服务,然后根据文档返回模拟数据,方便前端调试即可。
以上就是搭建一个CLI的过程,也比较简单,欢迎使用和点赞。