路由级中间件 #

一、路由级中间件概述 #

1.1 什么是路由级中间件? #

路由级中间件是绑定到Express Router实例上的中间件。使用 router.use()router.METHOD() 方法绑定。

1.2 与应用级中间件的区别 #

特性 应用级中间件 路由级中间件
绑定对象 app router
挂载方法 app.use() router.use()
作用范围 整个应用 特定路由模块
灵活性 全局生效 模块独立

1.3 基本用法 #

javascript
const express = require('express');
const router = express.Router();

router.use((req, res, next) => {
    console.log('路由级中间件');
    next();
});

router.get('/users', (req, res) => {
    res.json({ users: [] });
});

module.exports = router;

二、router.use() #

2.1 基本语法 #

javascript
router.use(path, callback)

2.2 路由模块中间件 #

javascript
const express = require('express');
const router = express.Router();

router.use((req, res, next) => {
    console.log('所有该路由模块的请求都会经过这里');
    next();
});

router.get('/', (req, res) => {
    res.json({ message: '用户列表' });
});

router.get('/:id', (req, res) => {
    res.json({ id: req.params.id });
});

module.exports = router;

2.3 路径匹配 #

javascript
router.use('/admin', (req, res, next) => {
    console.log('Admin路由');
    next();
});

router.get('/admin/dashboard', (req, res) => {
    res.json({ message: '管理面板' });
});

三、认证中间件 #

3.1 基本认证 #

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: '请先登录' });
    }
    
    try {
        const decoded = verifyToken(token);
        req.user = decoded;
        next();
    } catch (error) {
        return res.status(401).json({ error: '无效的token' });
    }
};

router.use(authMiddleware);

router.get('/profile', (req, res) => {
    res.json(req.user);
});

module.exports = router;

3.2 可选认证 #

javascript
const optionalAuth = (req, res, next) => {
    const token = req.headers['authorization'];
    
    if (token) {
        try {
            const decoded = verifyToken(token);
            req.user = decoded;
        } catch (error) {
            req.user = null;
        }
    }
    
    next();
};

router.use(optionalAuth);

router.get('/posts', (req, res) => {
    if (req.user) {
        res.json({ posts: [], message: '个性化内容' });
    } else {
        res.json({ posts: [], message: '公共内容' });
    }
});

3.3 角色权限 #

javascript
const checkRole = (...roles) => {
    return (req, res, next) => {
        if (!req.user) {
            return res.status(401).json({ error: '请先登录' });
        }
        
        if (!roles.includes(req.user.role)) {
            return res.status(403).json({ error: '无权限访问' });
        }
        
        next();
    };
};

router.get('/admin', authMiddleware, checkRole('admin'), (req, res) => {
    res.json({ message: '管理员页面' });
});

router.get('/dashboard', authMiddleware, checkRole('admin', 'editor'), (req, res) => {
    res.json({ message: '仪表盘' });
});

四、验证中间件 #

4.1 数据验证 #

javascript
const { body, validationResult } = require('express-validator');

const validateUser = [
    body('name').notEmpty().withMessage('用户名不能为空'),
    body('email').isEmail().withMessage('请输入有效的邮箱'),
    body('password').isLength({ min: 6 }).withMessage('密码至少6个字符'),
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }
        next();
    }
];

router.post('/users', validateUser, (req, res) => {
    res.status(201).json(req.body);
});

4.2 参数验证 #

javascript
const { param, validationResult } = require('express-validator');

const validateId = [
    param('id').isMongoId().withMessage('无效的ID格式'),
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }
        next();
    }
];

router.get('/users/:id', validateId, (req, res) => {
    res.json({ id: req.params.id });
});

4.3 查询参数验证 #

javascript
const { query, validationResult } = require('express-validator');

const validatePagination = [
    query('page').optional().isInt({ min: 1 }).toInt(),
    query('limit').optional().isInt({ min: 1, max: 100 }).toInt(),
    (req, res, next) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }
        next();
    }
];

router.get('/posts', validatePagination, (req, res) => {
    const { page = 1, limit = 10 } = req.query;
    res.json({ page, limit, posts: [] });
});

五、资源加载中间件 #

5.1 加载用户 #

javascript
const loadUser = async (req, res, next) => {
    try {
        const user = await User.findById(req.params.id);
        if (!user) {
            return res.status(404).json({ error: '用户不存在' });
        }
        req.targetUser = user;
        next();
    } catch (error) {
        next(error);
    }
};

router.get('/users/:id', loadUser, (req, res) => {
    res.json(req.targetUser);
});

router.put('/users/:id', loadUser, (req, res) => {
    Object.assign(req.targetUser, req.body);
    res.json(req.targetUser);
});

5.2 加载文章 #

javascript
const loadPost = async (req, res, next) => {
    try {
        const post = await Post.findById(req.params.postId);
        if (!post) {
            return res.status(404).json({ error: '文章不存在' });
        }
        req.post = post;
        next();
    } catch (error) {
        next(error);
    }
};

router.get('/posts/:postId', loadPost, (req, res) => {
    res.json(req.post);
});

router.get('/posts/:postId/comments', loadPost, (req, res) => {
    res.json(req.post.comments);
});

5.3 所有权检查 #

javascript
const checkOwnership = (req, res, next) => {
    if (req.post.author.toString() !== req.user.id) {
        return res.status(403).json({ error: '无权操作此资源' });
    }
    next();
};

router.put('/posts/:postId', 
    authMiddleware, 
    loadPost, 
    checkOwnership, 
    (req, res) => {
        Object.assign(req.post, req.body);
        res.json(req.post);
    }
);

六、路由特定中间件 #

6.1 单路由中间件 #

javascript
router.get('/protected', 
    authMiddleware, 
    (req, res) => {
        res.json({ user: req.user });
    }
);

6.2 多个中间件 #

javascript
router.delete('/users/:id',
    authMiddleware,
    checkRole('admin'),
    validateId,
    loadUser,
    (req, res) => {
        res.json({ message: '删除成功' });
    }
);

6.3 中间件数组 #

javascript
const middlewares = [
    authMiddleware,
    checkRole('admin'),
    validateId
];

router.delete('/users/:id', middlewares, loadUser, (req, res) => {
    res.json({ message: '删除成功' });
});

七、嵌套路由中间件 #

7.1 父路由中间件 #

javascript
const express = require('express');
const router = express.Router({ mergeParams: true });

router.use((req, res, next) => {
    console.log('postId:', req.params.postId);
    next();
});

router.get('/', (req, res) => {
    res.json({ 
        postId: req.params.postId, 
        comments: [] 
    });
});

module.exports = router;

7.2 评论路由 #

javascript
const express = require('express');
const router = express.Router();
const commentsRouter = require('./comments');

router.use('/:postId/comments', commentsRouter);

router.get('/', (req, res) => {
    res.json({ posts: [] });
});

module.exports = router;

八、完整示例 #

8.1 用户路由模块 #

javascript
const express = require('express');
const router = express.Router();
const { body, param, validationResult } = require('express-validator');
const User = require('../models/User');
const { auth, authorize } = require('../middlewares/auth');

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().trim().escape(),
    body('email').isEmail().normalizeEmail(),
    body('password').isLength({ min: 6 }),
    validate
];

const validateUserId = [
    param('id').isMongoId(),
    validate
];

const loadUser = async (req, res, next) => {
    try {
        const user = await User.findById(req.params.id);
        if (!user) {
            return res.status(404).json({ error: '用户不存在' });
        }
        req.targetUser = user;
        next();
    } catch (error) {
        next(error);
    }
};

router.get('/', auth, authorize('admin'), async (req, res, next) => {
    try {
        const users = await User.find().select('-password');
        res.json(users);
    } catch (error) {
        next(error);
    }
});

router.get('/:id', auth, validateUserId, loadUser, (req, res) => {
    res.json(req.targetUser);
});

router.post('/', validateUser, async (req, res, next) => {
    try {
        const user = new User(req.body);
        await user.save();
        res.status(201).json(user);
    } catch (error) {
        next(error);
    }
});

router.put('/:id', auth, validateUserId, loadUser, async (req, res, next) => {
    try {
        Object.assign(req.targetUser, req.body);
        await req.targetUser.save();
        res.json(req.targetUser);
    } catch (error) {
        next(error);
    }
});

router.delete('/:id', auth, authorize('admin'), validateUserId, loadUser, async (req, res, next) => {
    try {
        await req.targetUser.remove();
        res.status(204).send();
    } catch (error) {
        next(error);
    }
});

module.exports = router;

8.2 文章路由模块 #

javascript
const express = require('express');
const router = express.Router();
const Post = require('../models/Post');
const { auth } = require('../middlewares/auth');

router.get('/', async (req, res, next) => {
    try {
        const { page = 1, limit = 10 } = req.query;
        const posts = await Post.find()
            .skip((page - 1) * limit)
            .limit(parseInt(limit));
        res.json(posts);
    } catch (error) {
        next(error);
    }
});

router.get('/:id', async (req, res, next) => {
    try {
        const post = await Post.findById(req.params.id);
        if (!post) {
            return res.status(404).json({ error: '文章不存在' });
        }
        res.json(post);
    } catch (error) {
        next(error);
    }
});

router.post('/', auth, async (req, res, next) => {
    try {
        const post = new Post({
            ...req.body,
            author: req.user.id
        });
        await post.save();
        res.status(201).json(post);
    } catch (error) {
        next(error);
    }
});

router.put('/:id', auth, async (req, res, next) => {
    try {
        const post = await Post.findById(req.params.id);
        if (!post) {
            return res.status(404).json({ error: '文章不存在' });
        }
        
        if (post.author.toString() !== req.user.id) {
            return res.status(403).json({ error: '无权修改' });
        }
        
        Object.assign(post, req.body);
        await post.save();
        res.json(post);
    } catch (error) {
        next(error);
    }
});

router.delete('/:id', auth, async (req, res, next) => {
    try {
        const post = await Post.findById(req.params.id);
        if (!post) {
            return res.status(404).json({ error: '文章不存在' });
        }
        
        if (post.author.toString() !== req.user.id && req.user.role !== 'admin') {
            return res.status(403).json({ error: '无权删除' });
        }
        
        await post.remove();
        res.status(204).send();
    } catch (error) {
        next(error);
    }
});

module.exports = router;

九、总结 #

路由级中间件要点:

方法 说明
router.use() 路由模块级中间件
router.METHOD() 路由特定中间件
中间件数组 多个中间件组合
mergeParams 嵌套路由参数合并

下一步,让我们学习错误处理中间件!

最后更新:2026-03-28