前言
hello,同学们好👋。
刚开始用express搭建Web服务器时,大家是不是遇到了跟小凹一样的问题,还不是很了解两个对象:req(请求)和 res(响应)。路由写了一大堆,结果不是 req.body 读不出来,就是响应发了一半报错。
其实它们没那么复杂。花 10 分钟看完这篇文章,你会对这两个对象了如指掌。
本文基于 Express 4.x,Node.js 环境。所有代码均可直接运行验证。
一、Request 对象 ------ 客户端发来了什么?
每收到一个请求,Express 都会把请求的所有信息打包成一个 req 对象,作为第一个参数传给路由处理函数。
js
app.get('/some-path', (req, res) => {
// 所有请求相关的操作都通过 req 来获取
});
下面我们按使用频率,分组来看它的属性和方法。
1. 获取路由参数(req.params)
在路由路径中用 : 定义的动态部分,会收集在 req.params 里。
js
// 路由:/users/:userId/books/:bookId
app.get('/users/:userId/books/:bookId', (req, res) => {
console.log(req.params);
// 输出:{ userId: '123', bookId: '456' }
});
2. 获取查询字符串(req.query)
问号 ? 后面的参数,Express 会自动帮你转成对象。
js
// 请求:/search?keyword=express&page=2
app.get('/search', (req, res) => {
console.log(req.query.keyword); // 'express'
console.log(req.query.page); // '2'(注意:是字符串,不是数字)
});
3. 获取请求体(req.body)------ 重点
POST、PUT 请求提交的数据(JSON 或表单)都存在这里。但是 Express 默认不会解析 请求体,必须手动挂载中间件:
js
const express = require('express');
const app = express();
// 这两行中间件至关重要,缺一个都可能读不到数据
app.use(express.json()); // 解析 application/json
app.use(express.urlencoded({ extended: true })); // 解析表单数据
app.post('/login', (req, res) => {
console.log(req.body.username);
console.log(req.body.password);
});
⚠️ 新手最常踩的坑 :req.body 是 undefined,99% 是因为忘了加上面两行中间件。
4. 获取请求头(req.headers 和 req.get())
req.headers 是原始对象,键名统一为小写。推荐用 req.get() 方法,大小写不敏感。
js
app.get('/', (req, res) => {
// 两种方式都可以
const userAgent1 = req.headers['user-agent'];
const userAgent2 = req.get('User-Agent'); // 推荐
});
5. 获取 Cookie(需配合中间件)
Express 默认不解析 Cookie,需要安装 cookie-parser。
bash
npm install cookie-parser
js
const cookieParser = require('cookie-parser');
app.use(cookieParser()); // 普通 cookie
// app.use(cookieParser('秘钥')); // 带签名的 cookie
app.get('/', (req, res) => {
console.log(req.cookies); // { theme: 'dark', sessionId: 'abc' }
});
6. 其他常用属性速查
| 属性 | 说明 | 示例值 |
|---|---|---|
req.method |
请求方法 | 'GET'、'POST' |
req.url |
完整路径(含查询串) | '/users?sort=asc' |
req.path |
不含查询串的路径 | '/users' |
req.hostname |
主机名 | 'localhost' |
req.ip |
客户端 IP | '::1' 或 '192.168.1.1' |
req.protocol |
协议 | 'http' |
req.secure |
是否为 HTTPS | true / false |
req.xhr |
是否为 AJAX 请求 | true / false |
7. 常用方法
req.accepts(types):检查客户端能接受什么格式。
js
if (req.accepts('json')) {
res.json({ data: '...' });
} else {
res.send('...');
}
req.is(type):检查请求的Content-Type。
js
if (req.is('multipart/form-data')) {
// 处理文件上传
}
二、Response 对象 ------ 如何回复客户端?
res 对象代表服务器将要发给客户端的响应。任何一个路由处理函数都必须通过 res 来结束请求,否则请求会一直挂起。
1. 发送数据的三驾马车
res.send([body]) ------ 万能方法
根据传入数据的类型,自动设置合适的 Content-Type。
js
app.get('/test', (req, res) => {
// 字符串 → Content-Type: text/html
res.send('<h1>Hello World</h1>');
// 对象或数组 → Content-Type: application/json
res.send({ name: 'Express', version: 4 });
// Buffer → 触发文件下载
res.send(Buffer.from('hello'));
});
res.json([body]) ------ 明确返回 JSON
和 res.send(对象) 几乎一样,但语义更清晰。
js
app.get('/api/user', (req, res) => {
res.json({ id: 1, name: 'Alice' });
});
res.end([data]) ------ 底层结束方法
一般不直接使用,除非在处理原始流或 Buffer 时。
2. 文件操作
res.download(path [, filename]) ------ 提示浏览器下载
js
app.get('/download', (req, res) => {
res.download('/path/to/report.pdf', '2024-report.pdf');
});
res.sendFile(path [, options]) ------ 在浏览器中展示
适合图片、PDF 预览等。
js
app.get('/logo', (req, res) => {
res.sendFile('/public/logo.png', { root: __dirname });
});
3. 重定向
js
app.get('/old-page', (req, res) => {
res.redirect(301, '/new-page'); // 永久重定向
// 或 res.redirect('/new-page') 默认 302
});
4. 状态码与响应头
js
app.post('/login', (req, res) => {
if (!req.body.password) {
// 链式调用,先设状态码,再发数据
res.status(400).json({ error: '密码不能为空' });
}
});
js
// 设置单个响应头
res.set('Content-Type', 'text/plain');
// 批量设置
res.set({
'X-Custom-Header': 'value',
'Cache-Control': 'no-cache'
});
// 快捷设置 Content-Type
res.type('json'); // → 'application/json'
5. Cookie 操作
设置 Cookie 不需要额外中间件,直接用 res.cookie()。
js
app.get('/set-theme', (req, res) => {
res.cookie('theme', 'dark', {
maxAge: 900000, // 有效期(毫秒)
httpOnly: true, // 防止客户端 JS 读取(防 XSS)
secure: true, // 仅 HTTPS 下发送
signed: true // 需要配合 cookie-parser 的 secret
});
res.send('主题已设置为深色');
});
// 删除 Cookie
res.clearCookie('theme');
6. 一个重要属性:res.headersSent
用于检查响应头是否已经发送。可以避免重复发送响应头导致报错。
js
app.get('/check', (req, res) => {
console.log(res.headersSent); // false
res.send('ok');
console.log(res.headersSent); // true
});
⚠️ 致命错误 :响应一旦发送(调用了
send、json、redirect等方法),就不能再设置状态码或响应头,否则 Node.js 会抛出:
text
Error: Cannot set headers after they are sent to the client
三、总结速查表
Request 常用速查
| 我想获取什么 | 用这个 |
|---|---|
路由参数 /user/5 |
req.params.id |
查询参数 ?sort=asc |
req.query.sort |
| POST JSON / 表单数据 | req.body.field |
| 请求头 | req.get('Authorization') |
| Cookie | req.cookies.name(需中间件) |
| 请求方法 | req.method |
| 客户端 IP | req.ip |
Response 常用速查
| 我想做什么 | 用这个方法 |
|---|---|
| 返回 HTML / 文本 | res.send('...') |
| 返回 JSON | res.json({...}) |
| 设置 HTTP 状态码 | res.status(404).send('Not found') |
| 浏览器下载文件 | res.download('./file.pdf') |
| 重定向 | res.redirect('/new-url') |
| 设置响应头 | res.set('X-Header', 'value') |
| 设置 Cookie | res.cookie('key', 'value') |
| 删除 Cookie | res.clearCookie('key') |
写在最后
req 和 res 是 Express 的灵魂,80% 的日常开发都在和它们打交道。把这篇文章当作速查手册,遇到问题回来翻一翻,慢慢就熟了。
如果觉得有帮助,欢迎点赞收藏~也欢迎在评论区交流你踩过的坑 😊
本文基于 Express 4.x 版本整理,如有更新或出入请以官方文档为准。