express框架在node中是使用频率最高的,简单易上手,如果不合理的控制各个模块的划分,随着业务代码增多,项目就会变得难以维护(屎山)。
在写了多个屎山之后,我逐渐学习到了如何划分项目结构,合理拆分业务代码,极大的减少了屎山的出现
本文将介绍一种常见的 Express 应用结构:通过路由(Router)、控制器(Controller)、服务(Service)和中间件(Middleware)进行模块划分。
具体来说:
通过这种分层设计,代码的逻辑变得清晰,每一层都有明确的责任,降低了模块间的耦合度,方便后期的维护和扩展。
为了帮助大家更好地理解如何划分这些层次,我们将构建一个简单的任务管理 API,功能包括获取任务列表和创建任务。以下是我们的项目结构:
your_project/
│── public/ 静态文件目录
├── src/
│ ├── controllers/
│ │ ├── taskController.js # 处理请求的控制器
│ ├── services/
│ │ ├── taskService.js # 业务逻辑
│ ├── routes/
│ │ ├── taskRoutes.js # 路由
│ ├── middlewares/
│ │ ├── authMiddleware.js # 中间件
│ ├── models/
│ │ ├── taskModel.js # 数据模型(如 MongoDB model)
│ ├── app.js # Express 应用入口
│ └── config.js # 配置文件(如数据库连接等)
├── node_modules/
└── package.json
app.js
- 应用入口文件app.js
是 Express 应用的核心,负责加载路由和中间件,启动服务器。
const express = require("express");
const bodyParser = require("body-parser");
const taskRoutes = require("./routes/taskRoutes");
const cors = require("cors");
const app = express();
// 处理跨域
app.use(cors())
// 处理json请求
app.use(express.json())
// 处理表单类型
app.use(express.urlencoded({ extended: false }))
// 静态文件目录
app.use(express.static("./public"))
// 加载任务路由
app.use("/tasks", taskRoutes);
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ message: "Something went wrong" });
});
// 启动服务器
const port = 3000;
app.listen(port, () => {
console.log(`Server is running on http://localhost:${port}`);
});
routes/taskRoutes.js
- 路由层路由定义了请求路径和方法,它将请求委托给控制器处理。
const express = require("express");
const router = express.Router();
const taskController = require("../controllers/taskController");
const authMiddleware = require("../middlewares/authMiddleware");
// 路由:获取所有任务
router.get("/", authMiddleware, taskController.getAllTasks);
// 路由:创建任务
router.post("/", authMiddleware, taskController.createTask);
module.exports = router;
controllers/taskController.js
- 控制器层控制器负责处理请求,协调服务层的业务逻辑,并将处理结果返回客户端。
const taskService = require("../services/taskService");
const taskController = {
async getAllTasks(req, res, next) {
try {
const tasks = await taskService.getAllTasks();
res.status(200).json(tasks);
} catch (error) {
next(error); // 错误处理传递给错误处理中间件
}
},
async createTask(req, res, next) {
try {
const taskData = req.body;
const newTask = await taskService.createTask(taskData);
res.status(201).json(newTask);
} catch (error) {
next(error); // 错误处理传递给错误处理中间件
}
}
};
module.exports = taskController;
services/taskService.js
- 服务层服务层是核心业务逻辑处理的地方。它与数据库、外部 API 等交互,封装了业务操作。
const Task = require("../models/taskModel");
const taskService = {
async getAllTasks() {
try {
const tasks = await Task.find(); // 假设使用 MongoDB 查询所有任务
return tasks;
} catch (error) {
throw new Error("Error fetching tasks");
}
},
async createTask(taskData) {
try {
const newTask = new Task(taskData);
await newTask.save();
return newTask;
} catch (error) {
throw new Error("Error creating task");
}
}
};
module.exports = taskService;
models/taskModel.js
- 数据模型层在这里,我们定义了任务的数据结构,假设使用 MongoDB 作为数据库。
const mongoose = require("mongoose");
const taskSchema = new mongoose.Schema({
title: {
type: String,
required: true,
},
description: {
type: String,
required: true,
},
status: {
type: String,
default: "pending",
},
});
const Task = mongoose.model("Task", taskSchema);
module.exports = Task;
middlewares/authMiddleware.js
- 中间件层中间件负责在请求到达路由之前执行特定的任务,如身份验证、日志记录等。
const authMiddleware = (req, res, next) => {
// 假设我们做简单的身份验证
const authHeader = req.headers.authorization;
if (!authHeader || authHeader !== "Bearer your-token") {
return res.status(403).json({ message: "Unauthorized" });
}
next(); // 继续处理请求
};
module.exports = authMiddleware;
通过在 app.js
中定义全局的错误处理中间件,我们可以确保在应用发生任何错误时,客户端都能收到一致的错误响应。这种做法提高了代码的可维护性。
app.use((err, req, res, next) => {
console.error(err);
res.status(500).json({ message: "Something went wrong" });
});
通过将 Express 应用中的功能按路由、控制器、服务和中间件进行模块化,我们实现了一个清晰的架构。每一层都有明确的职责,互相之间的耦合度较低,方便后期的扩展和维护。具体来说: