应用结构 #
一、项目结构概述 #
1.1 为什么需要良好的结构? #
| 优势 | 说明 |
|---|---|
| 可维护性 | 代码易于查找和修改 |
| 可扩展性 | 方便添加新功能 |
| 可读性 | 团队成员易于理解 |
| 可测试性 | 便于编写单元测试 |
| 协作效率 | 减少团队沟通成本 |
1.2 结构设计原则 #
- 单一职责:每个文件只负责一个功能
- 模块化:功能按模块划分
- 分层架构:路由、控制器、服务、模型分离
- 配置分离:配置与代码分离
二、基础结构 #
2.1 简单项目结构 #
适用于小型项目或学习阶段:
text
my-app/
├── node_modules/
├── public/
│ ├── css/
│ ├── js/
│ └── images/
├── routes/
│ ├── index.js
│ └── users.js
├── views/
│ ├── index.ejs
│ └── layout.ejs
├── app.js
├── package.json
└── .env
2.2 app.js示例 #
javascript
const express = require('express');
const path = require('path');
const app = express();
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));
const indexRouter = require('./routes/index');
const usersRouter = require('./routes/users');
app.use('/', indexRouter);
app.use('/users', usersRouter);
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('出错了!');
});
module.exports = app;
三、MVC结构 #
3.1 目录结构 #
text
my-app/
├── config/
│ ├── database.js
│ ├── passport.js
│ └── index.js
├── controllers/
│ ├── authController.js
│ ├── userController.js
│ └── postController.js
├── models/
│ ├── User.js
│ ├── Post.js
│ └── Comment.js
├── routes/
│ ├── index.js
│ ├── authRoutes.js
│ ├── userRoutes.js
│ └── postRoutes.js
├── middlewares/
│ ├── auth.js
│ ├── validate.js
│ └── errorHandler.js
├── services/
│ ├── authService.js
│ ├── userService.js
│ └── emailService.js
├── utils/
│ ├── helpers.js
│ ├── constants.js
│ └── validators.js
├── views/
│ ├── layouts/
│ ├── partials/
│ ├── auth/
│ └── pages/
├── public/
│ ├── css/
│ ├── js/
│ └── images/
├── tests/
│ ├── unit/
│ └── integration/
├── app.js
├── server.js
├── package.json
└── .env
3.2 入口文件 #
server.js:
javascript
const app = require('./app');
const connectDB = require('./config/database');
connectDB();
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
});
app.js:
javascript
const express = require('express');
const path = require('path');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
const routes = require('./routes');
const errorHandler = require('./middlewares/errorHandler');
const app = express();
app.use(helmet());
app.use(cors());
app.use(morgan('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
app.use(express.static(path.join(__dirname, 'public')));
app.set('view engine', 'ejs');
app.set('views', path.join(__dirname, 'views'));
app.use('/api', routes);
app.use(errorHandler);
module.exports = app;
四、分层架构 #
4.1 路由层(Routes) #
负责定义API端点:
javascript
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
const { authenticate, validateUser } = require('../middlewares');
router.get('/', userController.getAllUsers);
router.get('/:id', userController.getUserById);
router.post('/', validateUser, userController.createUser);
router.put('/:id', authenticate, validateUser, userController.updateUser);
router.delete('/:id', authenticate, userController.deleteUser);
module.exports = router;
4.2 控制器层(Controllers) #
负责处理请求和响应:
javascript
const userService = require('../services/userService');
const userController = {
async getAllUsers(req, res, next) {
try {
const users = await userService.findAll();
res.json(users);
} catch (error) {
next(error);
}
},
async getUserById(req, res, next) {
try {
const { id } = req.params;
const user = await userService.findById(id);
if (!user) {
return res.status(404).json({ error: '用户不存在' });
}
res.json(user);
} catch (error) {
next(error);
}
},
async createUser(req, res, next) {
try {
const userData = req.body;
const user = await userService.create(userData);
res.status(201).json(user);
} catch (error) {
next(error);
}
},
async updateUser(req, res, next) {
try {
const { id } = req.params;
const userData = req.body;
const user = await userService.update(id, userData);
res.json(user);
} catch (error) {
next(error);
}
},
async deleteUser(req, res, next) {
try {
const { id } = req.params;
await userService.delete(id);
res.status(204).send();
} catch (error) {
next(error);
}
}
};
module.exports = userController;
4.3 服务层(Services) #
负责业务逻辑:
javascript
const User = require('../models/User');
const userService = {
async findAll() {
return await User.find().select('-password');
},
async findById(id) {
return await User.findById(id).select('-password');
},
async findByEmail(email) {
return await User.findOne({ email });
},
async create(userData) {
const user = new User(userData);
await user.save();
return user;
},
async update(id, userData) {
const user = await User.findByIdAndUpdate(
id,
{ $set: userData },
{ new: true, runValidators: true }
).select('-password');
return user;
},
async delete(id) {
await User.findByIdAndDelete(id);
}
};
module.exports = userService;
4.4 模型层(Models) #
负责数据定义:
javascript
const mongoose = require('mongoose');
const bcrypt = require('bcryptjs');
const userSchema = new mongoose.Schema({
name: {
type: String,
required: [true, '请输入用户名'],
trim: true,
maxlength: [50, '用户名不能超过50个字符']
},
email: {
type: String,
required: [true, '请输入邮箱'],
unique: true,
lowercase: true,
match: [/^\S+@\S+\.\S+$/, '请输入有效的邮箱地址']
},
password: {
type: String,
required: [true, '请输入密码'],
minlength: [6, '密码至少6个字符'],
select: false
},
role: {
type: String,
enum: ['user', 'admin'],
default: 'user'
},
createdAt: {
type: Date,
default: Date.now
}
});
userSchema.pre('save', async function(next) {
if (!this.isModified('password')) {
return next();
}
this.password = await bcrypt.hash(this.password, 10);
});
userSchema.methods.comparePassword = async function(password) {
return await bcrypt.compare(password, this.password);
};
module.exports = mongoose.model('User', userSchema);
五、中间件组织 #
5.1 认证中间件 #
javascript
const jwt = require('jsonwebtoken');
const User = require('../models/User');
const auth = async (req, res, next) => {
try {
const token = req.headers.authorization?.split(' ')[1];
if (!token) {
return res.status(401).json({ error: '请先登录' });
}
const decoded = jwt.verify(token, process.env.JWT_SECRET);
const user = await User.findById(decoded.id);
if (!user) {
return res.status(401).json({ error: '用户不存在' });
}
req.user = user;
next();
} catch (error) {
res.status(401).json({ error: '无效的token' });
}
};
const authorize = (...roles) => {
return (req, res, next) => {
if (!roles.includes(req.user.role)) {
return res.status(403).json({ error: '没有权限执行此操作' });
}
next();
};
};
module.exports = { auth, authorize };
5.2 验证中间件 #
javascript
const { validationResult } = require('express-validator');
const validate = (req, res, next) => {
const errors = validationResult(req);
if (!errors.isEmpty()) {
return res.status(400).json({ errors: errors.array() });
}
next();
};
const validateUser = [
body('name').notEmpty().withMessage('用户名不能为空'),
body('email').isEmail().withMessage('请输入有效的邮箱'),
body('password').isLength({ min: 6 }).withMessage('密码至少6个字符'),
validate
];
module.exports = { validate, validateUser };
5.3 错误处理中间件 #
javascript
const errorHandler = (err, req, res, next) => {
console.error(err.stack);
let statusCode = err.statusCode || 500;
let message = err.message || '服务器内部错误';
if (err.name === 'ValidationError') {
statusCode = 400;
message = Object.values(err.errors).map(e => e.message).join(', ');
}
if (err.name === 'CastError') {
statusCode = 400;
message = '无效的ID格式';
}
if (err.code === 11000) {
statusCode = 400;
message = '该数据已存在';
}
res.status(statusCode).json({
success: false,
error: message,
stack: process.env.NODE_ENV === 'development' ? err.stack : undefined
});
};
class AppError extends Error {
constructor(message, statusCode) {
super(message);
this.statusCode = statusCode;
this.isOperational = true;
Error.captureStackTrace(this, this.constructor);
}
}
module.exports = errorHandler;
module.exports.AppError = AppError;
六、配置管理 #
6.1 配置文件 #
javascript
const dotenv = require('dotenv');
dotenv.config({ path: `.env.${process.env.NODE_ENV || 'development'}` });
module.exports = {
port: parseInt(process.env.PORT, 10) || 3000,
env: process.env.NODE_ENV || 'development',
jwt: {
secret: process.env.JWT_SECRET,
expiresIn: process.env.JWT_EXPIRES_IN || '7d'
},
database: {
url: process.env.DATABASE_URL,
options: {
useNewUrlParser: true,
useUnifiedTopology: true
}
},
cors: {
origin: process.env.CORS_ORIGIN || '*',
methods: ['GET', 'POST', 'PUT', 'DELETE']
}
};
6.2 数据库配置 #
javascript
const mongoose = require('mongoose');
const config = require('./index');
const connectDB = async () => {
try {
await mongoose.connect(config.database.url, config.database.options);
console.log('数据库连接成功');
} catch (error) {
console.error('数据库连接失败:', error.message);
process.exit(1);
}
};
module.exports = connectDB;
七、工具函数 #
7.1 helpers.js #
javascript
const generateToken = (id) => {
return jwt.sign({ id }, process.env.JWT_SECRET, {
expiresIn: process.env.JWT_EXPIRES_IN
});
};
const filterObj = (obj, ...allowedFields) => {
const newObj = {};
Object.keys(obj).forEach(key => {
if (allowedFields.includes(key)) {
newObj[key] = obj[key];
}
});
return newObj;
};
const asyncHandler = (fn) => {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
};
module.exports = { generateToken, filterObj, asyncHandler };
7.2 constants.js #
javascript
const USER_ROLES = {
USER: 'user',
ADMIN: 'admin'
};
const HTTP_STATUS = {
OK: 200,
CREATED: 201,
NO_CONTENT: 204,
BAD_REQUEST: 400,
UNAUTHORIZED: 401,
FORBIDDEN: 403,
NOT_FOUND: 404,
INTERNAL_ERROR: 500
};
const ERROR_MESSAGES = {
USER_NOT_FOUND: '用户不存在',
INVALID_CREDENTIALS: '邮箱或密码错误',
UNAUTHORIZED: '请先登录',
FORBIDDEN: '没有权限'
};
module.exports = { USER_ROLES, HTTP_STATUS, ERROR_MESSAGES };
八、路由组织 #
8.1 路由索引 #
javascript
const express = require('express');
const router = express.Router();
const authRoutes = require('./authRoutes');
const userRoutes = require('./userRoutes');
const postRoutes = require('./postRoutes');
router.use('/auth', authRoutes);
router.use('/users', userRoutes);
router.use('/posts', postRoutes);
router.get('/health', (req, res) => {
res.json({ status: 'ok', timestamp: new Date().toISOString() });
});
module.exports = router;
8.2 版本控制 #
javascript
const express = require('express');
const app = express();
const v1Routes = require('./routes/v1');
const v2Routes = require('./routes/v2');
app.use('/api/v1', v1Routes);
app.use('/api/v2', v2Routes);
九、总结 #
应用结构要点:
| 层级 | 职责 |
|---|---|
| 路由层 | 定义API端点 |
| 控制器层 | 处理请求响应 |
| 服务层 | 业务逻辑 |
| 模型层 | 数据定义 |
| 中间件 | 请求处理 |
下一步,让我们深入学习Express的路由系统!
最后更新:2026-03-28