路由模块化 #

一、为什么需要模块化? #

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