eggjs 打造定制化mock工具

关于 egg 的基础,此处不做太多追述,如果不理解的可以看我之前写的文章---egg-从入门到上线

项目的构建

什么是mock

mock 的意思是模拟,也就是模拟接口返回的信息,用已有的信息替换它需要返回的信息,从实现对模块的测试。

当后端的接口还未完成,前端为了不影响工作效率,实现前后端分离,手动模拟后端接口

通用的方案 优点 缺点
json 书写简单,直接写一个js文件或者 json 文件,直接引入至项目中,完成后续代码编写 无法模拟数据的增删改查
json-server 模拟数据的增删改查 不能随机生成所需数据
mockjs 可随机生成所需数据,可模拟对数据的增删改查 代码入侵至项目中,需要维护
apifox 实现 mock 数据接口导入,可以直接导入 swagger 数据,代码入侵小 随机的数据对于项目的展示不打友好
node-service 通过 node 完成项目中的 mock 工具,自由度和数据完成极高 编写代码需要时间成本

项目的初衷

遇到项目中需要完整的演示出、上传、增删改查功能,对于后端接口的完成需要很长的一段时间(不确定的因素),产生了这个项目的初衷。所以如果你的项目,迭代紧凑,请谨慎选择。

关于数据存储设计

这一点考虑过 MongoDB、MySQL 等数据设计、但是根据业务场景出发,如果不能提供给我们数据库,那么该怎么办呢?那么我们就可以考虑通过做一个本地的 json 数据的增删改查设计,一样可以实现项目的运转,也可以考虑后期项目的迁移。

项目构建

bash 复制代码
npm i egg-init -g
egg-init egg-mock --type=simple   //例如:egg-init 项目名称 --type=simple
cd egg-mock
npm i

运行

arduino 复制代码
npm run dev  

你将会看到 hi-egg

部署

你仅仅需要将 NGINX 指向 127.0.0.1 即可完成部署

arduino 复制代码
npm run start // 启用
npm run stop // 停止

编写第一个接口

首先 egg 的流程是什么

router(路由) -> controller(控制层) -> service(业务逻辑层)

在controller/user.js中写上

javascript 复制代码
'use strict';

const Controller = require('egg').Controller;

class UserController extends Controller {
    async index() {
        const { ctx } = this;
        ctx.body = 'hi, user';
    }
}

module.exports = UserController;

router.js

javascript 复制代码
'use strict';

/**
 * @param {Egg.Application} app - egg application
 */
module.exports = app => {
  const { router, controller } = app;
  router.get('/', controller.home.index);
  router.get('/user', controller.user.index);
};

然后访问 127.0.0.1 => hi,user

第一个接口就完成了

拓展工具helper的封装

JSON 格式

  • JSON 指的是 JavaScript 对象表示法(J avaS cript O bject Notation)
  • JSON 是轻量级的文本数据交换格式

目录结构

arduino 复制代码
├── app // 项目中的配置
│   ├── controller // 控制层
│   │   ├── home.js
│   │   └── user.js
│   ├── public // 公共部分
│   └── router.js // 路由
├── extend // 拓展工具
│   │  helper.js // 帮助
├── config // 项目配置
│   ├── config.default.js
│   └── plugin.js // 插件
├── db // 数据 json
│   ├── user.json

help.js函数的封装

返回结构

在 helper 中可以引入一个 jsonFormat 的工具,如果返回status是 0 那么就是抛出异常

javascript 复制代码
const dayjs = require("dayjs");
module.exports = class Helper {
    // 处理 json 数据,默认是成功
    static async jsonFormat(row, status = 1) {
        // 如果是失败的情况下,则返回 0
        if (status === 0) {
          return {
            status: 0,
            message: row,
            timestamp: dayjs().format("YYYY-MM-DD HH:mm:ss"),
            success: false,
          };
        }

        return {
          status: 1,
          message: "成功",
          data: row,
          timestamp: dayjs().format("YYYY-MM-DD HH:mm:ss"),
          success: true,
        };
    }
}
js 复制代码
this.ctx.helper.jsonFormat('请求失败',0)

获取请求参数

vbnet 复制代码
/**
 * 
 * 可以使用ctx.params获取get或post请求参数
 */
module.exports = () => {
  return async function params(ctx, next) {
    ctx.params = {
      ...ctx.query,
      ...ctx.request.body
    }
    await next()
  }
}

db

这里的 db 文件夹,可以帮他想象出一个数据仓库,每一个 json 就是一张数据库的表

db/user.json

json 复制代码
[{
  "id":0,
  "name":"张三",
  "age": 18
},
{
  "id":1,
  "name":"李四",
  "age": 20
}]

这时候其实你也可以通过db/*.json来存储各种表的数据,在未来如果有数据库了,也可以存储在MongoDB 或者 mysql 数据中作为数据转换的过度与拓展。

构建 CURD 的封装

首先 JSON 是什么,它其实就是一组数组对象首先要获取到这个 json 数据对吧

json获取数据操作

extend/helper.js

javascript 复制代码
const { readFile, writeFile } = require("fs"); // 解构出并加载fs模块中的两个方法
const { join } = require('path')

// 获取到 json 数据的位置
const path = (name) => join(__dirname, `../../db/${name}.json`)

module.exports = class Helper {
  // 读取数据
  static async readFile(name) {
    const filename = path(name);
    return new Promise((resolve, reject) => {
      readFile(filename, "utf-8", (err, data) => {
        if (err) reject(err);
        const res = JSON.parse(data); // 把JSON转成JS数组
        console.log(res);
        resolve(res);
      });
    });
  }

  // 写入数据
  static async writeFile(name, param) {
    const filename = path(name);
    return new Promise((resolve,reject) => {
      writeFile(filename, JSON.stringify(param), (err) => {
        if (err) reject(err);
        resolve("修改成功");
      });
    });
  }
};

总结JSON 作为数据源的处理

其实 JSON 的 CURD 本质上,就是就是针对于数组的CURD 方法,然后读写进入文件,这样的话,就可以持久化保持数据。

查询接口

设计接口

首先这里有一个知识点,设计接口的时候,要注意什么?没错,设计的时候,要注意 MVC 的设计

MVC 模式

MVC 模式代表 Model-View-Controller(模型-视图-控制器) 模式。这种模式用于应用程序的分层开发。

Model(模型) - 模型代表一个存取数据的对象或 JAVA POJO。它也可以带有逻辑,在数据变化时更新控制器。

View(视图) - 视图代表模型包含的数据的可视化。

Controller(控制器) - 控制器作用于模型和视图上。它控制数据流向模型对象,并在数据变化时更新视图。它使视图与模型分离开。

那么为什么呢?为前端,有时候很难理解 MVC 的设计架构,其实就像咱们得组件化管理一样,如果把所有的组件放在一个组件中,那势必会难以维护,分层的作用,就可以达到复用性的效果。

对于 egg 来说

M 就是 service

V 就是 public

C 就是 controller

我们通过路由进入controller,然后调用 service的过程,就是接口执行的过程。

查询全部接口

以下所有的 helper 工具,都在拓展工具helper的封装所写,可以查看该文

router.js

arduino 复制代码
// R 读取(Read)
router.get("/user", controller.user.read);

controller/user.js

js 复制代码
 async read() {
    const { ctx } = this;
    try {
      const res = await ctx.service.user.read();
      ctx.body = await ctx.helper.jsonFormat(res);
    } catch (error) {
      ctx.body = await ctx.helper.jsonFormat(error, 0);
    }
  }

service/user.js

csharp 复制代码
// 读取所有的数据
async read() {
    try {
      return await this.ctx.helper.query("user");
    } catch (error) {
      return error;
    }
  }

extend/helper.js

javascript 复制代码
  // 查询
  static async query(name) {
    try {
      return await this.readFile(name);
    } catch (error) {
      return error;
    }
  }

接口测试

输入接口地址

在 user.json中填写

json 复制代码
[
  {
    "id": 0,
    "name":"张三",
    "age": 20
  }
]

那么这时候的返回值是

json 复制代码
{
    "status": 1,
    "message": "成功",
    "data": [
        {
            "id": "张三",
            "age": 2
        }
    ],
    "timestamp": "2022-08-26 21:00:32",
    "success": true
}

查询id 的接口

router.js

csharp 复制代码
// R 读取(Read)
router.get("/user/:id", controller.user.readId);

controller/user.js

js 复制代码
async readId() {
    const { ctx } = this;
    const { id } = ctx.params;
    try {
      const res = await ctx.service.user.readId(id);
      ctx.body = await ctx.helper.jsonFormat(res || {});
    } catch (error) {
      ctx.body = await ctx.helper.jsonFormat(error, 0);
    }
}

service/user.js

js 复制代码
async readId(id) {
    try {
      return await this.ctx.helper.queryId("user", id);
    } catch (error) {
      return error;
    }
}

extend/helper.js

javascript 复制代码
 // 查询id,将 id 传入
  static async queryId(name, id) {
    try {
      const res = await this.readFile(name);
      // 这里会遇到一个问题,id 有可能是 number、string所以统一转成 number
      const info = res.find((item) => +item.id === +id);
    } catch (error) {
      return error;
    }
  }

新增、更新、删除接口

首先要解决 cor 跨域

config/config.default.js

ini 复制代码
config.security = {
    domainWhiteList: ["*"],
    csrf: {
      enable: false,
    },
  };

新增接口

router.js

csharp 复制代码
// C 创建(Create)
router.post("/user", controller.user.add);

controller/user.js

csharp 复制代码
// 新增数据
async add() {
    const { ctx } = this;
    try {
    // 将外部传入的数据都传入,此处也可以做一些校验
      const res = await ctx.service.user.add(ctx.request.body);
      ctx.body = await ctx.helper.jsonFormat(res);
    } catch (error) {
      ctx.body = await ctx.helper.jsonFormat(error, 0);
    }
}  

service/user.js

csharp 复制代码
 async add(row) {
    try {
      return await this.ctx.helper.add("user", row);
    } catch (error) {
      return error;
    }
  }

extend/helper.js

javascript 复制代码
// 添加
  static async add(name, row) {
    try {
      const res = this.readFile(name);
      // 如果是一个空的 json 数组,就给 id赋值0
      row.id = 0;
      // 如果是有数据的,那就自增
      if (res.length !== 0) {
        const id = res[res.length - 1].id + 1;
        row.id = id;
      }
      res.push(row);
      // 写入数据
      await this.writeFile(res);
      return "添加成功";
    } catch (error) {
      return error;
    }
  }

extend/helper.js

javascript 复制代码
// 添加
  static async add(name, row) {
    try {
      const res = await this.readFile(name);
      // 如果是一个空的 json 数组,就给 id赋值0
      row.id = 0;
      // 如果是有数据的,那就自增
      if (res.length !== 0) {
        const id = res[res.length - 1].id + 1;
        row.id = id;
      }
      res.push(row);
      console.log(res);
      // 写入数据
      await this.writeFile(name, res);
      return "添加成功";
    } catch (error) {
      return error;
    }
  }

接口测试

删除数据

router.js

go 复制代码
// D 删除(Delete)
router.delete("/user/:id", controller.user.delete);

controller/user.js

csharp 复制代码
// 删除接口
 async delete() {
    const { ctx } = this;
    const { id } = ctx.params; // 传入一个 id 即可
    try {
      const res = await ctx.service.user.del(id);
      ctx.body = await ctx.helper.jsonFormat(res);
    } catch (error) {
      ctx.body = await ctx.helper.jsonFormat(error, 0);
    }
  }

service/user.js

javascript 复制代码
async del(id) {
    try {
      return await this.ctx.helper.queryId("user", id);
    } catch (error) {
      return error;
    }
  }

extend/helper.js

javascript 复制代码
static async del(name, id) {
    try {
      // 获取整张数据
      const res = await this.readFile(name);
      //  过滤掉这个 id 的数据,返回成一个新的数组
      const result = res.filter((item) => +item.id !== +id);
      // 写入数据
      await this.writeFile(name, result);
      return "删除成功";
    } catch (error) {
      return error;
    }
  }

接口测试

更新接口

router.js

arduino 复制代码
// U 更新(Update)
router.put("/user", controller.user.update);

controller/user.js

csharp 复制代码
// 更新数据
async update() {
    const { ctx } = this;
    try {
      const res = await ctx.service.user.update(ctx.request.body);
      ctx.body = await ctx.helper.jsonFormat(res);
    } catch (error) {
      ctx.body = await ctx.helper.jsonFormat(error, 0);
    }
  }

service/user.js

javascript 复制代码
async update(row) {
    try {
      return await this.ctx.helper.update("user", row);
    } catch (error) {
      return error;
    }
  }

extend/helper.js

javascript 复制代码
 static async update(name, param) {
    try {
      // 获取整张数据
      const res = await this.readFile(name);
      // 找到你想修改值的id,所在的位置
      const index = res.findIndex((item) => +item.id === +param.id);
      // 用想修改的值,直接替换掉数据
      res.splice(index, 1, { ...res[index], ...param });
      // 写入数据
      await this.writeFile(name, res);
      return "修改成功";
    } catch (error) {
      return error;
    }
  }

接口测试

代码地址

github.com/masonjs-cn/...

相关推荐
hackeroink1 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者3 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart
向前看-3 小时前
验证码机制
前端·后端
燃先生._.4 小时前
Day-03 Vue(生命周期、生命周期钩子八个函数、工程化开发和脚手架、组件化开发、根组件、局部注册和全局注册的步骤)
前端·javascript·vue.js
高山我梦口香糖5 小时前
[react]searchParams转普通对象
开发语言·前端·javascript
m0_748235245 小时前
前端实现获取后端返回的文件流并下载
前端·状态模式
m0_748240256 小时前
前端如何检测用户登录状态是否过期
前端
black^sugar6 小时前
纯前端实现更新检测
开发语言·前端·javascript
寻找沙漠的人7 小时前
前端知识补充—CSS
前端·css