Response对象 #

一、Response对象概述 #

1.1 什么是Response对象? #

Response对象(通常简写为res)代表了HTTP响应。通过它可以向客户端发送响应数据、设置响应头、状态码等。

1.2 基本结构 #

javascript
app.get('/', (req, res) => {
    res.status(200);
    res.set('Content-Type', 'application/json');
    res.json({ message: 'Hello' });
});

二、响应方法 #

2.1 res.send() #

发送各种类型的响应:

javascript
app.get('/text', (req, res) => {
    res.send('文本响应');
});

app.get('/html', (req, res) => {
    res.send('<h1>HTML响应</h1>');
});

app.get('/json', (req, res) => {
    res.send({ message: 'JSON响应' });
});

app.get('/buffer', (req, res) => {
    res.send(Buffer.from('Buffer响应'));
});

2.2 res.json() #

发送JSON响应:

javascript
app.get('/api/users', (req, res) => {
    res.json({
        success: true,
        data: [
            { id: 1, name: '张三' },
            { id: 2, name: '李四' }
        ]
    });
});

app.get('/error', (req, res) => {
    res.status(400).json({
        success: false,
        error: '请求参数错误'
    });
});

2.3 res.status() #

设置HTTP状态码:

javascript
app.get('/ok', (req, res) => {
    res.status(200).send('成功');
});

app.get('/created', (req, res) => {
    res.status(201).json({ message: '资源创建成功' });
});

app.get('/no-content', (req, res) => {
    res.status(204).send();
});

app.get('/bad-request', (req, res) => {
    res.status(400).json({ error: '错误的请求' });
});

app.get('/unauthorized', (req, res) => {
    res.status(401).json({ error: '未授权' });
});

app.get('/forbidden', (req, res) => {
    res.status(403).json({ error: '禁止访问' });
});

app.get('/not-found', (req, res) => {
    res.status(404).json({ error: '资源未找到' });
});

app.get('/error', (req, res) => {
    res.status(500).json({ error: '服务器错误' });
});

2.4 res.redirect() #

重定向:

javascript
app.get('/old', (req, res) => {
    res.redirect('/new');
});

app.get('/new', (req, res) => {
    res.send('新页面');
});

app.get('/external', (req, res) => {
    res.redirect('https://expressjs.com');
});

app.get('/permanent', (req, res) => {
    res.redirect(301, '/new-location');
});

app.get('/temporary', (req, res) => {
    res.redirect(302, '/temporary-location');
});

2.5 res.render() #

渲染模板:

javascript
app.set('view engine', 'ejs');

app.get('/', (req, res) => {
    res.render('index', { 
        title: '首页',
        message: '欢迎来到Express'
    });
});

app.get('/user/:id', (req, res) => {
    res.render('user', {
        userId: req.params.id,
        user: { name: '张三', email: 'zhangsan@example.com' }
    });
});

2.6 res.sendFile() #

发送文件:

javascript
const path = require('path');

app.get('/download', (req, res) => {
    const filePath = path.join(__dirname, 'files', 'document.pdf');
    res.sendFile(filePath);
});

app.get('/download-with-options', (req, res) => {
    const filePath = path.join(__dirname, 'files', 'document.pdf');
    res.sendFile(filePath, {
        root: __dirname,
        dotfiles: 'deny',
        headers: {
            'x-timestamp': Date.now(),
            'x-sent': true
        }
    });
});

2.7 res.download() #

触发浏览器下载:

javascript
app.get('/download-file', (req, res) => {
    const filePath = path.join(__dirname, 'files', 'report.pdf');
    res.download(filePath);
});

app.get('/download-custom-name', (req, res) => {
    const filePath = path.join(__dirname, 'files', 'report.pdf');
    res.download(filePath, '月度报告.pdf');
});

app.get('/download-callback', (req, res) => {
    const filePath = path.join(__dirname, 'files', 'report.pdf');
    res.download(filePath, (err) => {
        if (err) {
            console.error('下载失败:', err);
        } else {
            console.log('下载成功');
        }
    });
});

2.8 res.end() #

结束响应:

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

app.get('/end-data', (req, res) => {
    res.end('响应结束');
});

三、响应头 #

3.1 res.set() / res.header() #

设置响应头:

javascript
app.get('/headers', (req, res) => {
    res.set('Content-Type', 'application/json');
    res.set('X-Custom-Header', 'value');
    res.json({ message: 'Headers set' });
});

app.get('/multiple-headers', (req, res) => {
    res.set({
        'Content-Type': 'application/json',
        'Cache-Control': 'no-cache',
        'X-Powered-By': 'Express'
    });
    res.json({ message: 'Multiple headers set' });
});

3.2 res.get() #

获取已设置的响应头:

javascript
app.get('/get-header', (req, res) => {
    res.set('Content-Type', 'application/json');
    const contentType = res.get('Content-Type');
    res.json({ contentType });
});

3.3 res.append() #

追加响应头:

javascript
app.get('/append-header', (req, res) => {
    res.append('Set-Cookie', 'name=express');
    res.append('Set-Cookie', 'lang=zh');
    res.send('Cookies set');
});

3.4 res.removeHeader() #

删除响应头:

javascript
app.get('/remove-header', (req, res) => {
    res.removeHeader('X-Powered-By');
    res.send('Header removed');
});

四、Cookie操作 #

4.1 res.cookie() #

设置Cookie:

javascript
app.get('/set-cookie', (req, res) => {
    res.cookie('name', 'express');
    res.send('Cookie已设置');
});

app.get('/set-cookie-options', (req, res) => {
    res.cookie('token', 'abc123', {
        maxAge: 900000,
        httpOnly: true,
        secure: true,
        sameSite: 'strict',
        domain: 'example.com',
        path: '/admin',
        signed: true
    });
    res.send('Cookie已设置');
});

Cookie选项:

选项 说明
maxAge 过期时间(毫秒)
expires 过期日期
httpOnly 仅HTTP访问
secure 仅HTTPS
sameSite 同站策略
domain 域名
path 路径
signed 签名

4.2 res.clearCookie() #

清除Cookie:

javascript
app.get('/clear-cookie', (req, res) => {
    res.clearCookie('name');
    res.send('Cookie已清除');
});

app.get('/clear-cookie-options', (req, res) => {
    res.clearCookie('token', {
        path: '/admin',
        domain: 'example.com'
    });
    res.send('Cookie已清除');
});

五、链式调用 #

5.1 方法链 #

javascript
app.get('/chain', (req, res) => {
    res.status(200)
       .set('Content-Type', 'application/json')
       .set('X-Custom-Header', 'value')
       .json({ message: 'Success' });
});

5.2 完整示例 #

javascript
app.get('/api/users/:id', (req, res) => {
    const user = getUserById(req.params.id);
    
    if (!user) {
        return res.status(404)
                  .json({ error: '用户不存在' });
    }
    
    res.status(200)
       .set('Cache-Control', 'public, max-age=3600')
       .json({
           success: true,
           data: user
       });
});

六、响应格式化 #

6.1 统一响应格式 #

javascript
app.use((req, res, next) => {
    res.success = (data, message = '成功') => {
        res.json({
            success: true,
            data,
            message
        });
    };
    
    res.error = (message, code = 400) => {
        res.status(code).json({
            success: false,
            error: message
        });
    };
    
    res.paginate = (data, total, page, limit) => {
        res.json({
            success: true,
            data,
            pagination: {
                total,
                page,
                limit,
                totalPages: Math.ceil(total / limit)
            }
        });
    };
    
    next();
});

app.get('/users', (req, res) => {
    res.success({ users: [] }, '获取用户列表成功');
});

app.get('/error', (req, res) => {
    res.error('出错了', 400);
});

app.get('/posts', (req, res) => {
    res.paginate([], 100, 1, 10);
});

6.2 API响应封装 #

javascript
class ApiResponse {
    static success(res, data, message = '成功', statusCode = 200) {
        return res.status(statusCode).json({
            success: true,
            data,
            message
        });
    }
    
    static error(res, message, statusCode = 400, errors = null) {
        return res.status(statusCode).json({
            success: false,
            error: message,
            errors
        });
    }
    
    static paginated(res, data, total, page, limit) {
        return res.json({
            success: true,
            data,
            pagination: {
                total,
                page,
                limit,
                totalPages: Math.ceil(total / limit)
            }
        });
    }
    
    static created(res, data, message = '创建成功') {
        return this.success(res, data, message, 201);
    }
    
    static noContent(res) {
        return res.status(204).send();
    }
    
    static notFound(res, message = '资源未找到') {
        return this.error(res, message, 404);
    }
    
    static unauthorized(res, message = '未授权') {
        return this.error(res, message, 401);
    }
    
    static forbidden(res, message = '禁止访问') {
        return this.error(res, message, 403);
    }
}

app.get('/users', (req, res) => {
    ApiResponse.success(res, { users: [] });
});

app.post('/users', (req, res) => {
    ApiResponse.created(res, req.body);
});

app.get('/users/:id', (req, res) => {
    const user = null;
    if (!user) {
        return ApiResponse.notFound(res, '用户不存在');
    }
    ApiResponse.success(res, user);
});

七、内容协商 #

7.1 res.format() #

根据Accept头返回不同格式:

javascript
app.get('/data', (req, res) => {
    const data = { message: 'Hello' };
    
    res.format({
        'application/json': () => {
            res.json(data);
        },
        'text/html': () => {
            res.send(`<h1>${data.message}</h1>`);
        },
        'text/plain': () => {
            res.send(data.message);
        },
        'default': () => {
            res.status(406).send('Not Acceptable');
        }
    });
});

7.2 多格式API #

javascript
app.get('/users/:id', (req, res) => {
    const user = { id: req.params.id, name: '张三' };
    
    res.format({
        json: () => res.json(user),
        html: () => res.render('user', { user }),
        xml: () => {
            res.type('application/xml');
            res.send(`<user><id>${user.id}</id><name>${user.name}</name></user>`);
        },
        default: () => res.json(user)
    });
});

八、流式响应 #

8.1 流式输出 #

javascript
app.get('/stream', (req, res) => {
    res.set('Content-Type', 'text/event-stream');
    res.set('Cache-Control', 'no-cache');
    res.set('Connection', 'keep-alive');
    
    let counter = 0;
    const interval = setInterval(() => {
        res.write(`data: ${JSON.stringify({ count: counter++ })}\n\n`);
        
        if (counter >= 10) {
            clearInterval(interval);
            res.end();
        }
    }, 1000);
    
    req.on('close', () => {
        clearInterval(interval);
    });
});

8.2 文件流 #

javascript
const fs = require('fs');

app.get('/video', (req, res) => {
    const videoPath = path.join(__dirname, 'video.mp4');
    const stat = fs.statSync(videoPath);
    const fileSize = stat.size;
    const range = req.headers.range;
    
    if (range) {
        const parts = range.replace(/bytes=/, '').split('-');
        const start = parseInt(parts[0], 10);
        const end = parts[1] ? parseInt(parts[1], 10) : fileSize - 1;
        const chunksize = (end - start) + 1;
        const file = fs.createReadStream(videoPath, { start, end });
        
        res.writeHead(206, {
            'Content-Range': `bytes ${start}-${end}/${fileSize}`,
            'Accept-Ranges': 'bytes',
            'Content-Length': chunksize,
            'Content-Type': 'video/mp4'
        });
        
        file.pipe(res);
    } else {
        res.writeHead(200, {
            'Content-Length': fileSize,
            'Content-Type': 'video/mp4'
        });
        
        fs.createReadStream(videoPath).pipe(res);
    }
});

九、缓存控制 #

9.1 设置缓存头 #

javascript
app.get('/cached', (req, res) => {
    res.set('Cache-Control', 'public, max-age=3600');
    res.json({ message: '可缓存1小时' });
});

app.get('/no-cache', (req, res) => {
    res.set('Cache-Control', 'no-cache, no-store, must-revalidate');
    res.json({ message: '不缓存' });
});

app.get('/etag', (req, res) => {
    const data = { message: 'Hello' };
    const etag = require('crypto')
        .createHash('md5')
        .update(JSON.stringify(data))
        .digest('hex');
    
    if (req.headers['if-none-match'] === etag) {
        return res.status(304).end();
    }
    
    res.set('ETag', etag);
    res.json(data);
});

十、完整示例 #

javascript
const express = require('express');
const path = require('path');
const app = express();

app.use(express.json());

app.use((req, res, next) => {
    res.success = (data, message = '成功') => {
        res.json({ success: true, data, message });
    };
    res.error = (message, code = 400) => {
        res.status(code).json({ success: false, error: message });
    };
    next();
});

app.get('/', (req, res) => {
    res.success({ message: 'Welcome to Express API' });
});

app.get('/users', (req, res) => {
    res.success([
        { id: 1, name: '张三' },
        { id: 2, name: '李四' }
    ]);
});

app.get('/users/:id', (req, res) => {
    res.success({ id: req.params.id, name: '张三' });
});

app.post('/users', (req, res) => {
    res.status(201).success(req.body, '用户创建成功');
});

app.put('/users/:id', (req, res) => {
    res.success({ id: req.params.id, ...req.body });
});

app.delete('/users/:id', (req, res) => {
    res.status(204).send();
});

app.get('/download', (req, res) => {
    const file = path.join(__dirname, 'files', 'document.pdf');
    res.download(file);
});

app.use((req, res) => {
    res.status(404).error('路由未找到', 404);
});

app.use((err, req, res, next) => {
    console.error(err.stack);
    res.status(500).error('服务器错误', 500);
});

app.listen(3000);

十一、总结 #

Response对象要点:

方法 说明
res.send() 发送响应
res.json() 发送JSON
res.status() 设置状态码
res.redirect() 重定向
res.render() 渲染模板
res.sendFile() 发送文件
res.download() 下载文件
res.set() 设置响应头
res.cookie() 设置Cookie
res.format() 内容协商

下一步,让我们学习处理表单数据!

最后更新:2026-03-28