关于 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;
}
}