路由参数 #

一、参数类型 #

1.1 Express中的参数类型 #

类型 获取方式 示例
路径参数 req.params /users/:id
查询参数 req.query /search?q=express
请求体参数 req.body POST请求体
请求头参数 req.headers Authorization

二、路径参数 #

2.1 基本用法 #

使用冒号 : 定义路径参数:

javascript
app.get('/users/:id', (req, res) => {
    console.log(req.params);
    res.json({ userId: req.params.id });
});

访问 /users/123

json
{ "userId": "123" }

2.2 多个路径参数 #

javascript
app.get('/posts/:postId/comments/:commentId', (req, res) => {
    const { postId, commentId } = req.params;
    res.json({
        postId,
        commentId,
        message: `文章${postId}的评论${commentId}`
    });
});

访问 /posts/1/comments/5

json
{
    "postId": "1",
    "commentId": "5",
    "message": "文章1的评论5"
}

2.3 可选参数 #

使用 ? 表示可选参数:

javascript
app.get('/books/:category/:id?', (req, res) => {
    const { category, id } = req.params;
    
    if (id) {
        res.json({ category, id, message: '获取单本书' });
    } else {
        res.json({ category, message: '获取分类列表' });
    }
});

2.4 参数验证 #

使用正则表达式验证参数格式:

javascript
app.get('/users/:id(\\d+)', (req, res) => {
    res.json({ userId: req.params.id });
});

只有数字ID才会匹配,如 /users/123

javascript
app.get('/files/:filename(\\w+\\.\\w+)', (req, res) => {
    res.json({ filename: req.params.filename });
});

匹配文件名格式,如 /files/document.pdf

2.5 app.param()中间件 #

为特定参数添加预处理:

javascript
app.param('id', (req, res, next, id) => {
    console.log(`处理ID参数: ${id}`);
    
    if (!/^\d+$/.test(id)) {
        return res.status(400).json({ error: 'ID必须是数字' });
    }
    
    req.userId = parseInt(id);
    next();
});

app.get('/users/:id', (req, res) => {
    res.json({ userId: req.userId });
});

2.6 参数转换 #

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

app.get('/users/:userId', (req, res) => {
    res.json(req.user);
});

三、查询参数 #

3.1 基本用法 #

javascript
app.get('/search', (req, res) => {
    const { q, page, limit } = req.query;
    res.json({ query: q, page, limit });
});

访问 /search?q=express&page=2&limit=10

json
{
    "query": "express",
    "page": "2",
    "limit": "10"
}

3.2 默认值处理 #

javascript
app.get('/products', (req, res) => {
    const page = parseInt(req.query.page) || 1;
    const limit = parseInt(req.query.limit) || 10;
    const sort = req.query.sort || 'createdAt';
    const order = req.query.order || 'desc';
    
    res.json({ page, limit, sort, order });
});

3.3 数组参数 #

javascript
app.get('/filter', (req, res) => {
    let { tags } = req.query;
    
    if (tags && !Array.isArray(tags)) {
        tags = [tags];
    }
    
    res.json({ tags: tags || [] });
});

访问 /filter?tags=node&tags=express

json
{
    "tags": ["node", "express"]
}

3.4 对象参数 #

Express支持对象形式的查询参数:

javascript
app.get('/search', (req, res) => {
    res.json(req.query);
});

访问 /search?user[name]=张三&user[age]=25

json
{
    "user": {
        "name": "张三",
        "age": "25"
    }
}

3.5 复杂查询构建 #

javascript
app.get('/products', async (req, res) => {
    const {
        category,
        minPrice,
        maxPrice,
        inStock,
        brand,
        sort,
        page = 1,
        limit = 10
    } = req.query;
    
    const query = {};
    
    if (category) query.category = category;
    if (minPrice || maxPrice) {
        query.price = {};
        if (minPrice) query.price.$gte = parseFloat(minPrice);
        if (maxPrice) query.price.$lte = parseFloat(maxPrice);
    }
    if (inStock !== undefined) query.inStock = inStock === 'true';
    if (brand) query.brand = brand;
    
    const sortOption = {};
    if (sort) {
        const [field, order] = sort.split(':');
        sortOption[field] = order === 'desc' ? -1 : 1;
    }
    
    res.json({
        query,
        sort: sortOption,
        pagination: { page: parseInt(page), limit: parseInt(limit) }
    });
});

四、请求体参数 #

4.1 JSON请求体 #

javascript
app.use(express.json());

app.post('/users', (req, res) => {
    const { name, email, age } = req.body;
    res.json({ name, email, age });
});

请求:

bash
curl -X POST http://localhost:3000/users \
  -H "Content-Type: application/json" \
  -d '{"name":"张三","email":"zhangsan@example.com","age":25}'

4.2 URL编码请求体 #

javascript
app.use(express.urlencoded({ extended: true }));

app.post('/login', (req, res) => {
    const { username, password } = req.body;
    res.json({ username });
});

4.3 表单数据 #

javascript
app.post('/register', (req, res) => {
    const { name, email, password, confirmPassword } = req.body;
    
    if (password !== confirmPassword) {
        return res.status(400).json({ error: '两次密码不一致' });
    }
    
    res.status(201).json({ name, email });
});

4.4 文件上传 #

javascript
const multer = require('multer');
const upload = multer({ dest: 'uploads/' });

app.post('/upload', upload.single('avatar'), (req, res) => {
    res.json({
        file: req.file,
        body: req.body
    });
});

五、请求头参数 #

5.1 获取请求头 #

javascript
app.get('/headers', (req, res) => {
    const userAgent = req.headers['user-agent'];
    const contentType = req.headers['content-type'];
    const authorization = req.headers['authorization'];
    
    res.json({ userAgent, contentType, authorization });
});

5.2 Authorization头 #

javascript
app.get('/protected', (req, res) => {
    const authHeader = req.headers['authorization'];
    
    if (!authHeader) {
        return res.status(401).json({ error: '缺少认证信息' });
    }
    
    const token = authHeader.split(' ')[1];
    
    res.json({ token });
});

5.3 自定义请求头 #

javascript
app.get('/api/data', (req, res) => {
    const apiVersion = req.headers['x-api-version'];
    const clientId = req.headers['x-client-id'];
    
    res.json({ apiVersion, clientId });
});

5.4 Content-Type处理 #

javascript
app.post('/data', (req, res) => {
    const contentType = req.headers['content-type'];
    
    if (contentType !== 'application/json') {
        return res.status(415).json({ error: '不支持的媒体类型' });
    }
    
    res.json(req.body);
});

六、Cookie参数 #

6.1 安装cookie-parser #

bash
npm install cookie-parser

6.2 基本用法 #

javascript
const cookieParser = require('cookie-parser');
app.use(cookieParser());

app.get('/set-cookie', (req, res) => {
    res.cookie('name', 'express', { 
        maxAge: 900000, 
        httpOnly: true 
    });
    res.json({ message: 'Cookie已设置' });
});

app.get('/get-cookie', (req, res) => {
    res.json({ cookies: req.cookies });
});

6.3 签名Cookie #

javascript
app.use(cookieParser('secret-key'));

app.get('/set-signed', (req, res) => {
    res.cookie('token', 'abc123', { signed: true });
    res.json({ message: '签名Cookie已设置' });
});

app.get('/get-signed', (req, res) => {
    res.json({ signedCookies: req.signedCookies });
});

七、参数验证 #

7.1 手动验证 #

javascript
app.post('/users', (req, res) => {
    const { name, email, password, age } = req.body;
    const errors = [];
    
    if (!name || name.trim().length === 0) {
        errors.push('用户名不能为空');
    }
    
    const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
    if (!email || !emailRegex.test(email)) {
        errors.push('请输入有效的邮箱地址');
    }
    
    if (!password || password.length < 6) {
        errors.push('密码至少6个字符');
    }
    
    if (age && (isNaN(age) || age < 0 || age > 150)) {
        errors.push('请输入有效的年龄');
    }
    
    if (errors.length > 0) {
        return res.status(400).json({ errors });
    }
    
    res.status(201).json({ name, email, age });
});

7.2 使用express-validator #

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

const validateUser = [
    body('name')
        .notEmpty().withMessage('用户名不能为空')
        .isLength({ min: 2, max: 50 }).withMessage('用户名2-50个字符'),
    body('email')
        .isEmail().withMessage('请输入有效的邮箱地址')
        .normalizeEmail(),
    body('password')
        .isLength({ min: 6 }).withMessage('密码至少6个字符')
        .matches(/\d/).withMessage('密码必须包含数字'),
    body('age')
        .optional()
        .isInt({ min: 0, max: 150 }).withMessage('年龄必须在0-150之间')
];

app.post('/users', validateUser, (req, res) => {
    const errors = validationResult(req);
    
    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
    }
    
    res.status(201).json(req.body);
});

7.3 路径参数验证 #

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

app.get('/users/:id', 
    param('id').isMongoId().withMessage('无效的用户ID'),
    (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }
        res.json({ userId: req.params.id });
    }
);

7.4 查询参数验证 #

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

app.get('/search',
    query('q').notEmpty().withMessage('搜索关键词不能为空'),
    query('page').optional().isInt({ min: 1 }).toInt(),
    query('limit').optional().isInt({ min: 1, max: 100 }).toInt(),
    (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }
        res.json(req.query);
    }
);

八、参数清理 #

8.1 数据清洗 #

javascript
const sanitizeInput = (req, res, next) => {
    if (req.body) {
        Object.keys(req.body).forEach(key => {
            if (typeof req.body[key] === 'string') {
                req.body[key] = req.body[key].trim();
            }
        });
    }
    next();
};

app.use(sanitizeInput);

8.2 使用express-validator清理 #

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

app.post('/users', [
    body('name').trim().escape(),
    body('email').normalizeEmail(),
    body('age').toInt()
], (req, res) => {
    res.json(req.body);
});

九、完整示例 #

9.1 用户API #

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

const app = express();
app.use(express.json());

let users = [
    { id: 1, name: '张三', email: 'zhangsan@example.com', age: 25 },
    { id: 2, name: '李四', email: 'lisi@example.com', age: 30 }
];

app.get('/users', 
    query('page').optional().isInt({ min: 1 }).toInt().default(1),
    query('limit').optional().isInt({ min: 1, max: 100 }).toInt().default(10),
    (req, res) => {
        const { page, limit } = req.query;
        const start = (page - 1) * limit;
        const end = start + limit;
        
        res.json({
            page,
            limit,
            total: users.length,
            data: users.slice(start, end)
        });
    }
);

app.get('/users/:id',
    param('id').isInt({ min: 1 }).withMessage('无效的用户ID').toInt(),
    (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }
        
        const user = users.find(u => u.id === req.params.id);
        if (!user) {
            return res.status(404).json({ error: '用户不存在' });
        }
        
        res.json(user);
    }
);

app.post('/users', [
    body('name').notEmpty().withMessage('用户名不能为空').trim(),
    body('email').isEmail().withMessage('请输入有效的邮箱').normalizeEmail(),
    body('age').optional().isInt({ min: 0, max: 150 }).toInt()
], (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
    }
    
    const newUser = {
        id: users.length + 1,
        ...req.body
    };
    
    users.push(newUser);
    res.status(201).json(newUser);
});

app.put('/users/:id', [
    param('id').isInt({ min: 1 }).toInt(),
    body('name').optional().notEmpty().trim(),
    body('email').optional().isEmail().normalizeEmail(),
    body('age').optional().isInt({ min: 0, max: 150 }).toInt()
], (req, res) => {
    const errors = validationResult(req);
    if (!errors.isEmpty()) {
        return res.status(400).json({ errors: errors.array() });
    }
    
    const index = users.findIndex(u => u.id === req.params.id);
    if (index === -1) {
        return res.status(404).json({ error: '用户不存在' });
    }
    
    users[index] = { ...users[index], ...req.body };
    res.json(users[index]);
});

app.delete('/users/:id',
    param('id').isInt({ min: 1 }).toInt(),
    (req, res) => {
        const errors = validationResult(req);
        if (!errors.isEmpty()) {
            return res.status(400).json({ errors: errors.array() });
        }
        
        const index = users.findIndex(u => u.id === req.params.id);
        if (index === -1) {
            return res.status(404).json({ error: '用户不存在' });
        }
        
        users.splice(index, 1);
        res.status(204).send();
    }
);

app.listen(3000);

十、总结 #

路由参数要点:

参数类型 获取方式 用途
路径参数 req.params 资源标识
查询参数 req.query 过滤、分页
请求体 req.body 创建、更新数据
请求头 req.headers 认证、元信息
Cookie req.cookies 会话管理

下一步,让我们学习路由模块化!

最后更新:2026-03-28