💌手把手架构Express划分Router、Controller、Service和Middleware
背景👹
express框架在node中是使用频率最高的,简单易上手,如果不合理的控制各个模块的划分,随着业务代码增多,项目就会变得难以维护(屎山💩)。
在写了多个屎山之后,我逐渐学习到了如何划分项目结构,合理拆分业务代码😹,极大的减少了屎山💩的出现
本文将介绍一种常见的 Express 应用结构:通过路由(Router)、控制器(Controller)、服务(Service)和中间件(Middleware)进行模块划分。
为什么要划分路由、控制器、服务和中间件?🙈
具体来说:
- 路由(Router) 就像是马路,它为请求提供了道路(路径)和方向(HTTP 方法),决定了请求会走哪条路,怎么走。
- 控制器(Controller) 就像是交警,它处理来自路上(即客户端)的请求,确保车辆(请求)按正确的规则(业务逻辑)走,并且最终交付正确的结果(响应)。交警(控制器)可能会根据需要协调不同的司机(服务层)来完成任务。
- 服务(Service) 就像是司机,负责实际的操作和执行任务,比如开车(处理核心业务逻辑)。司机会根据道路(控制器的指示)完成具体的工作,可能需要与外部环境(数据库、API 等)互动。
- 中间件(Middleware) 就像是红绿灯或路障,它们在车辆(请求)到达目标之前,提供额外的功能,比如控制通行(权限验证、日志记录等),确保路上顺畅或者在需要时拦停车辆,阻止不符合规则的请求继续前行。
通过这种分层设计,代码的逻辑变得清晰,每一层都有明确的责任,降低了模块间的耦合度,方便后期的维护和扩展。
Express 项目结构设计🙉
为了帮助大家更好地理解如何划分这些层次,我们将构建一个简单的任务管理 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 应用中的功能按路由、控制器、服务和中间件进行模块化,我们实现了一个清晰的架构。每一层都有明确的职责,互相之间的耦合度较低,方便后期的扩展和维护。具体来说:
- 路由(Router) :负责定义路径和 HTTP 方法。(马路)
- 控制器(Controller) :负责处理请求和响应,调用服务层的业务逻辑。(交警)
- 服务(Service) :封装了核心业务逻辑,负责与数据库或外部 API 的交互。(司机)
- 中间件(Middleware) :提供额外的功能,如身份验证、请求日志等。(红绿灯路障)