Koa Mongoose
一、认识
在 Node.js
应用中, 常见的服务架构一般采用分层(或 MVC
)的方式,将各个职责区分开来,主要包括以下几个层次:
-
路由层
Routes
: 负责将请求路由到对应的控制器,是入口层。负责接收客户端请求,并根据URL
和HTTP
方法将请求分发到对应的控制器方法。// 使用 koa-router 定义路由
const Router = require('koa-router');
const positionRouter = new Router();
// 定义一个 POST 路由,带有动态参数 id
positionRouter.post("/editItem/:id", editItem);
module.exports = positionRouter; -
控制器层
Controllers
: 解析请求、调用业务逻辑、处理响应和错误,是接口层。作为请求与业务逻辑之间的中介。主要负责解析请求参数、调用业务逻辑(可能在Service
层或直接调用Model
),然后组织响应返回给客户端。比如说: 获取路由参数(例如ctx.params
)和请求体(ctx.request.body
); 校验参数合法性; 处理异常,并根据业务逻辑组织响应数据async function editItem(ctx) {
const id = ctx.params.id; // 从 URL 获取 id
const data = ctx.request.body; // 从请求体中获取其它数据
try {
// 调用数据层或者业务层逻辑进行更新操作
await PositionModel.updateOne({ _id: id }, data);
ctx.body = { code: 200, message: "编辑成功" };
} catch (error) {
ctx.body = { code: 500, message: "编辑失败", error: error.message };
}
} -
数据模型层
Model
: 封装数据库操作,定义数据模型,负责数据的持久化。主要负责与数据库进行交互,封装了对数据的增删改查操作。通常使用ORM/ODM
(如Mongoose
对于MongoDB
、Sequelize
对于SQL
数据库)来定义数据模型(Schema
)和操作方法。比如: 定义数据结构和数据校验规则; 封装数据库操作,隐藏数据库实现细节。const mongoose = require('mongoose');
// 定义 Schema
const PositionSchema = new mongoose.Schema({
title: { type: String, required: true },
description: String,
salary: Number,
// ...其它字段
});
// 创建 Model
const PositionModel = mongoose.model('Position', PositionSchema);
module.exports = PositionModel; -
Service
层: 聚合复杂业务逻辑,解耦控制器和数据层,提高代码的可测试性和可维护性。进一步分离业务逻辑,将复杂的业务操作从控制器中抽离出来,使控制器只关注请求和响应,业务逻辑则由Service
层完成。比如: 组合多个数据操作,处理事务或复杂的业务流程; 对外暴露统一的业务接口,便于复用和单元测试。// userService.js
const PositionModel = require('../models/Position');
async function updatePosition(id, data) {
// 可以包含更多复杂的业务逻辑
return await PositionModel.updateOne({ _id: id }, data);
}
module.exports = { updatePosition };控制器中调用:
const { updatePosition } = require('../services/positionService');
async function editItem(ctx) {
const id = ctx.params.id;
const data = ctx.request.body;
try {
await updatePosition(id, data);
ctx.body = { code: 200, message: "编辑成功" };
} catch (error) {
ctx.body = { code: 500, message: "编辑失败", error: error.message };
}
} -
中间件
Middleware
: 提供通用功能(如请求解析、日志、权限验证),贯穿整个请求生命周期。在请求处理链中提供额外功能,比如请求体解析、日志记录、权限验证、错误处理等。Koa
或Express
等框架都支持中间件机制,允许在路由之前或之后插入公共逻辑。const Koa = require('koa');
const bodyParser = require('koa-bodyparser');
const app = new Koa();
// 使用中间件解析请求体
app.use(bodyParser());
// 挂载路由
const positionRouter = require('./routes/position');
app.use(positionRouter.routes());
二、主程序
const Koa = require("koa");
const KoaBody = require("koa-body");
const mongoose = require("mongoose");
const positionRouter = require("./router/position");
const port = 3000;
const app = new Koa();
const { koaBody } = KoaBody;
mongoose.connect("mongodb://127.0.0.1:27017/bolawen");
mongoose.connection.on("connected", function () {
console.log("MongoDB 启动成功!!!");
});
mongoose.connection.on("error", function (error) {
console.log("MongoDB 出现错误, 错误为:", error);
});
mongoose.connection.on("disconnected", function () {
console.log("MongoDB 断开连接!!!");
});
app.use(
koaBody({
text: true,
json: true,
multipart: true,
urlencoded: true,
encoding: "gzip",
})
);
app.use(positionRouter.routes()).use(positionRouter.allowedMethods());
app.listen(port, () => {
console.log("Koa 服务启动成功!!!");
});
三、路由层
const KoaRouter = require("koa-router");
const {
getList,
addItem,
editItem,
getDetail,
removeItem,
} = require("../controller/position");
const positionRouter = new KoaRouter({ prefix: "/position" });
positionRouter.get("/getList", getList);
positionRouter.get("/getDetail", getDetail);
positionRouter.get("/getDetail/:id", getDetail);
positionRouter.post("/addItem", addItem);
positionRouter.post("/editItem", editItem);
positionRouter.post("/editItem/:id", editItem);
positionRouter.post("/removeItem", removeItem);
positionRouter.post("/removeItem/:id", removeItem);
module.exports = positionRouter;
四、控制器层
const PositionModel = require("../model/position");
async function getList(ctx) {
const page = parseInt(ctx.query.page, 10) || 1;
const limit = parseInt(ctx.query.limit, 10) || 10;
const skip = (page - 1) * limit;
const [list, total] = await Promise.all([
PositionModel.find().skip(skip).limit(limit),
PositionModel.countDocuments(),
]);
if (list) {
ctx.body = {
code: 200,
data: {
list,
pageInfo: {
page,
limit,
total,
},
},
message: "查询成功!!",
};
return;
}
ctx.body = {
code: 200,
message: "查询失败!!",
};
}
async function getDetail(ctx) {
const id = ctx.params.id || ctx.query.id;
const result = await PositionModel.findById(id);
if (result) {
ctx.body = {
code: 200,
data: result,
message: "查询成功!!",
};
return;
}
ctx.body = {
code: 200,
message: "查询失败!!",
};
}
async function addItem(ctx) {
const { body } = ctx.request;
const result = await PositionModel.create(body);
if (result) {
ctx.body = {
code: 200,
message: "添加成功!!",
};
return;
}
ctx.body = {
code: 200,
message: "添加失败!!",
};
}
async function editItem(ctx) {
const { body } = ctx.request;
const id = ctx.params.id || body.id;
const { stock, price, quantity } = body;
const result = await PositionModel.findByIdAndUpdate(id, {
stock,
price,
quantity,
});
if (result) {
ctx.body = {
code: 200,
message: "编辑成功!!",
};
return;
}
ctx.body = {
code: 200,
message: "编辑失败!!",
};
}
async function removeItem(ctx) {
const { body } = ctx.request;
const id = ctx.params.id || body.id;
const result = await PositionModel.findByIdAndDelete(id);
if (result) {
ctx.body = {
code: 200,
message: "删除成功!!",
};
return;
}
ctx.body = {
code: 200,
message: "删除失败!!",
};
}
module.exports = {
getList,
addItem,
editItem,
getDetail,
removeItem,
};
五、数据模型层
const mongoose = require("mongoose");
const PositionSchema = new mongoose.Schema({
stock: { type: String, required: true },
price: { type: Number, required: true },
account: { type: String, required: true },
quantity: { type: Number, required: true },
});
const PositionModel = mongoose.model("Position", PositionSchema);
module.exports = PositionModel;