路由模块化 #
一、为什么需要模块化? #
1.1 问题场景 #
当应用变大时,所有路由写在一个文件中会变得难以维护:
javascript
app.get('/users', ...);
app.get('/users/:id', ...);
app.post('/users', ...);
app.get('/posts', ...);
app.get('/posts/:id', ...);
app.get('/posts/:id/comments', ...);
app.get('/products', ...);
1.2 模块化的好处 #
| 好处 | 说明 |
|---|---|
| 可维护性 | 路由按功能分离,易于查找和修改 |
| 可读性 | 代码结构清晰,易于理解 |
| 复用性 | 路由模块可在不同应用中复用 |
| 协作性 | 团队成员可独立开发不同模块 |
二、express.Router() #
2.1 创建路由模块 #
routes/users.js:
javascript
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json({ users: [] });
});
router.get('/:id', (req, res) => {
res.json({ id: req.params.id });
});
router.post('/', (req, res) => {
res.status(201).json(req.body);
});
module.exports = router;
2.2 使用路由模块 #
app.js:
javascript
const express = require('express');
const app = express();
const usersRouter = require('./routes/users');
app.use('/users', usersRouter);
app.listen(3000);
2.3 路由路径说明 #
路由模块中的路径会与挂载路径合并:
javascript
app.use('/users', usersRouter);
| 模块中的路由 | 最终路径 |
|---|---|
| router.get(‘/’) | GET /users |
| router.get(‘/:id’) | GET /users/:id |
| router.post(‘/’) | POST /users |
三、路由模块组织 #
3.1 按功能划分 #
text
routes/
├── index.js
├── auth.js
├── users.js
├── posts.js
├── products.js
└── orders.js
routes/index.js:
javascript
const express = require('express');
const router = express.Router();
const authRouter = require('./auth');
const usersRouter = require('./users');
const postsRouter = require('./posts');
const productsRouter = require('./products');
const ordersRouter = require('./orders');
router.use('/auth', authRouter);
router.use('/users', usersRouter);
router.use('/posts', postsRouter);
router.use('/products', productsRouter);
router.use('/orders', ordersRouter);
module.exports = router;
app.js:
javascript
const express = require('express');
const app = express();
const routes = require('./routes');
app.use('/api', routes);
app.listen(3000);
3.2 嵌套路由 #
routes/posts.js:
javascript
const express = require('express');
const router = express.Router();
const commentsRouter = require('./comments');
router.get('/', (req, res) => {
res.json({ posts: [] });
});
router.get('/:id', (req, res) => {
res.json({ id: req.params.id });
});
router.use('/:postId/comments', commentsRouter);
module.exports = router;
routes/comments.js:
javascript
const express = require('express');
const router = express.Router();
router.get('/', (req, res) => {
res.json({
postId: req.params.postId,
comments: []
});
});
router.post('/', (req, res) => {
res.status(201).json({
postId: req.params.postId,
comment: req.body
});
});
module.exports = router;
访问路径:
- GET
/api/posts- 获取文章列表 - GET
/api/posts/:id- 获取单篇文章 - GET
/api/posts/:postId/comments- 获取评论列表 - POST
/api/posts/:postId/comments- 创建评论
四、路由中间件 #
4.1 路由级中间件 #
javascript
const express = require('express');
const router = express.Router();
router.use((req, res, next) => {
console.log('路由中间件 - ', req.method, req.url);
next();
});
router.get('/', (req, res) => {
res.json({ message: '用户列表' });
});
module.exports = router;
4.2 认证中间件 #
javascript
const express = require('express');
const router = express.Router();
const authMiddleware = (req, res, next) => {
const token = req.headers['authorization'];
if (!token) {
return res.status(401).json({ error: '请先登录' });
}
req.user = { id: 1, name: '用户' };
next();
};
router.use(authMiddleware);
router.get('/profile', (req, res) => {
res.json(req.user);
});
module.exports = router;
4.3 特定路由中间件 #
javascript
const express = require('express');
const router = express.Router();
const validateUser = (req, res, next) => {
const { name, email } = req.body;
if (!name || !email) {
return res.status(400).json({ error: '缺少必填字段' });
}
next();
};
router.get('/', (req, res) => {
res.json({ users: [] });
});
router.post('/', validateUser, (req, res) => {
res.status(201).json(req.body);
});
module.exports = router;
五、完整示例 #
5.1 项目结构 #
text
my-app/
├── routes/
│ ├── index.js
│ ├── auth.js
│ ├── users.js
│ └── posts.js
├── middlewares/
│ ├── auth.js
│ └── validate.js
├── controllers/
│ ├── authController.js
│ ├── userController.js
│ └── postController.js
└── app.js
5.2 路由文件 #
routes/index.js:
javascript
const express = require('express');
const router = express.Router();
const authRouter = require('./auth');
const usersRouter = require('./users');
const postsRouter = require('./posts');
router.use('/auth', authRouter);
router.use('/users', usersRouter);
router.use('/posts', postsRouter);
router.get('/health', (req, res) => {
res.json({ status: 'ok' });
});
module.exports = router;
routes/auth.js:
javascript
const express = require('express');
const router = express.Router();
const authController = require('../controllers/authController');
router.post('/register', authController.register);
router.post('/login', authController.login);
router.post('/logout', authController.logout);
router.post('/refresh-token', authController.refreshToken);
module.exports = router;
routes/users.js:
javascript
const express = require('express');
const router = express.Router();
const userController = require('../controllers/userController');
const { auth, authorize } = require('../middlewares/auth');
router.get('/', auth, authorize('admin'), userController.getAllUsers);
router.get('/:id', auth, userController.getUserById);
router.put('/:id', auth, userController.updateUser);
router.delete('/:id', auth, authorize('admin'), userController.deleteUser);
module.exports = router;
routes/posts.js:
javascript
const express = require('express');
const router = express.Router();
const postController = require('../controllers/postController');
const { auth } = require('../middlewares/auth');
const { validatePost } = require('../middlewares/validate');
router.get('/', postController.getAllPosts);
router.get('/:id', postController.getPostById);
router.post('/', auth, validatePost, postController.createPost);
router.put('/:id', auth, validatePost, postController.updatePost);
router.delete('/:id', auth, postController.deletePost);
module.exports = router;
5.3 控制器文件 #
controllers/userController.js:
javascript
const User = require('../models/User');
const userController = {
async getAllUsers(req, res, next) {
try {
const users = await User.find().select('-password');
res.json(users);
} catch (error) {
next(error);
}
},
async getUserById(req, res, next) {
try {
const user = await User.findById(req.params.id).select('-password');
if (!user) {
return res.status(404).json({ error: '用户不存在' });
}
res.json(user);
} catch (error) {
next(error);
}
},
async updateUser(req, res, next) {
try {
const user = await User.findByIdAndUpdate(
req.params.id,
{ $set: req.body },
{ new: true, runValidators: true }
).select('-password');
if (!user) {
return res.status(404).json({ error: '用户不存在' });
}
res.json(user);
} catch (error) {
next(error);
}
},
async deleteUser(req, res, next) {
try {
const user = await User.findByIdAndDelete(req.params.id);
if (!user) {
return res.status(404).json({ error: '用户不存在' });
}
res.status(204).send();
} catch (error) {
next(error);
}
}
};
module.exports = userController;
5.4 中间件文件 #
middlewares/auth.js:
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 };
middlewares/validate.js:
javascript
const { body, 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 validatePost = [
body('title').notEmpty().withMessage('标题不能为空'),
body('content').notEmpty().withMessage('内容不能为空'),
validate
];
module.exports = { validate, validatePost };
5.5 主应用文件 #
app.js:
javascript
const express = require('express');
const helmet = require('helmet');
const cors = require('cors');
const morgan = require('morgan');
require('dotenv').config();
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('/api', routes);
app.use(errorHandler);
const PORT = process.env.PORT || 3000;
app.listen(PORT, () => {
console.log(`服务器运行在端口 ${PORT}`);
});
六、API版本控制 #
6.1 URL版本控制 #
text
routes/
├── v1/
│ ├── index.js
│ ├── users.js
│ └── posts.js
└── v2/
├── index.js
├── users.js
└── posts.js
app.js:
javascript
const v1Routes = require('./routes/v1');
const v2Routes = require('./routes/v2');
app.use('/api/v1', v1Routes);
app.use('/api/v2', v2Routes);
6.2 头部版本控制 #
javascript
app.use('/api', (req, res, next) => {
const version = req.headers['accept-version'] || 'v1';
req.apiVersion = version;
next();
});
app.get('/api/users', (req, res) => {
if (req.apiVersion === 'v2') {
return res.json({ users: [], version: 'v2' });
}
res.json({ users: [], version: 'v1' });
});
七、路由合并 #
7.1 合并参数 #
javascript
const router = express.Router({ mergeParams: true });
router.get('/comments', (req, res) => {
res.json({ postId: req.params.postId, comments: [] });
});
7.2 区分大小写 #
javascript
const router = express.Router({ caseSensitive: true });
router.get('/Users', (req, res) => {
res.json({ message: '区分大小写' });
});
7.3 严格匹配 #
javascript
const router = express.Router({ strict: true });
router.get('/users', (req, res) => {
res.json({ message: '严格匹配,/users/ 不会匹配' });
});
八、最佳实践 #
8.1 路由命名规范 #
javascript
router.get('/users', getUsers);
router.get('/users/:id', getUserById);
router.post('/users', createUser);
router.put('/users/:id', updateUser);
router.delete('/users/:id', deleteUser);
8.2 统一响应格式 #
javascript
router.get('/users', async (req, res, next) => {
try {
const users = await User.find();
res.json({
success: true,
data: users,
message: '获取成功'
});
} catch (error) {
next(error);
}
});
8.3 错误处理 #
javascript
const asyncHandler = fn => (req, res, next) =>
Promise.resolve(fn(req, res, next)).catch(next);
router.get('/users', asyncHandler(async (req, res) => {
const users = await User.find();
res.json(users);
}));
九、总结 #
路由模块化要点:
| 概念 | 说明 |
|---|---|
| express.Router() | 创建路由模块 |
| app.use() | 挂载路由模块 |
| 嵌套路由 | 路由模块嵌套 |
| 路由中间件 | 路由级别的中间件 |
| 版本控制 | API版本管理 |
下一步,让我们深入学习中间件!
最后更新:2026-03-28