有了这个mock工具,开发速度提升一倍:原理篇

379 阅读3分钟

一、前言

开发了个Mock CLI工具,助力开发速度提升一倍,欢迎使用和点赞:

支持能力

  • Mock API
  • 读取Swagger文档,并生成API接口
  • 无需担心跨域问题,在反代理时自行在本地配置
  • 安装方便:node环境一行指令安装/也支持Docker启动
  • API生成读取顺序:本地mock -> swagger -> remote api

在开发过程中,也遇到了一些难点,做一下复盘

  • ESM引入和Require引入的差异导致一些写法的不同
  • 关于API路径匹配问题
  • Swagger版本不同,json文件字段的不同
  • 通过Github构建不同的Docker镜像,发送Docker hub
  • 通过配置代理和中间件来转发不同的请求

二、实现原理图

1、原理图

image.png

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的过程,也比较简单,欢迎使用和点赞。