请求对象与响应对象

在 Express 应用中,每个路由处理函数都会接收到两个核心对象:请求对象 (req)响应对象 (res)。它们分别封装了来自客户端的请求信息和用于构建响应的各种方法。理解并熟练使用这两个对象是掌握 Express 的关键。

1. 请求对象 (req)

请求对象 req 包含了客户端发送的所有信息,例如 URL 参数、查询字符串、请求头、请求体等。它是对 Node.js 原生 http.IncomingMessage 的扩展,添加了许多便利属性和方法。

1.1 常用属性

属性 说明
req.params 包含路由参数的对象(例如 /users/:id 中的 id)。
req.query 包含查询字符串参数的对象(例如 ?name=John 中的 { name: 'John' })。
req.body 包含请求体的数据(需要 express.json()express.urlencoded() 等中间件解析)。
req.headers 包含请求头的对象,所有头名称都是小写。
req.method HTTP 请求方法(GET、POST 等)。
req.url 完整的请求 URL(包含路径和查询字符串)。
req.path 请求的路径部分(不含查询字符串)。
req.baseUrl 路由挂载的基路径(如果使用了路由挂载)。
req.originalUrl 原始的请求 URL(包含基路径和查询字符串)。
req.hostname 客户端请求的主机名(从 Host 头解析)。
req.ip 客户端的 IP 地址。
req.protocol 请求协议(httphttps)。
req.secure 布尔值,表示是否通过 HTTPS 连接(req.protocol === 'https' 的快捷方式)。
req.cookies 包含 cookie 的对象(需要 cookie-parser 中间件)。
req.signedCookies 包含签名 cookie 的对象(需要 cookie-parser 并传入密钥)。

1.2 常用方法

方法 说明
req.get(field) 获取指定请求头的值(不区分大小写)。
req.is(type) 检查请求的 Content-Type 是否匹配给定的 MIME 类型。
req.accepts(types) 检查客户端可接受的响应类型(基于 Accept 头),返回最佳匹配。
req.acceptsCharsets() 检查可接受的字符集。
req.acceptsEncodings() 检查可接受的编码(如 gzip)。
req.acceptsLanguages() 检查可接受的语言。
req.range(size) 解析 Range 头,用于分片下载。

1.3 请求对象示例

app.get('/users/:id', (req, res) => {
  console.log('路由参数:', req.params);      // 例如 { id: '123' }
  console.log('查询参数:', req.query);       // 例如 { name: 'John' }
  console.log('请求头:', req.headers);
  console.log('方法:', req.method);
  console.log('路径:', req.path);
  console.log('主机名:', req.hostname);
  console.log('IP:', req.ip);

  // 获取特定请求头
  const userAgent = req.get('User-Agent');
  console.log('User-Agent:', userAgent);

  res.send('OK');
});

2. 响应对象 (res)

响应对象 res 用于构建并发送 HTTP 响应给客户端。它扩展了 Node.js 原生 http.ServerResponse,提供了更便捷的 API。

2.1 常用方法

方法 说明
res.send(body) 发送响应,自动设置 Content-LengthContent-Type。body 可以是字符串、对象、Buffer 等。
res.json(body) 发送 JSON 响应,自动设置 Content-Type: application/json 并将 body JSON 序列化。
res.status(code) 设置 HTTP 状态码,返回 res 自身以支持链式调用。
res.set(field, value) 设置响应头,也可以接受一个对象同时设置多个头。
res.end([data]) 结束响应过程,可以发送可选数据(通常用于无需 send 的底层操作)。
res.redirect([status,] path) 重定向到指定路径,默认状态码 302。
res.render(view, [locals], callback) 渲染视图模板,并发送渲染后的 HTML。
res.sendFile(path, [options], [callback]) 以流式方式发送文件,自动处理 Content-Type 和 Content-Disposition。
res.download(path, [filename], [callback]) 提示客户端下载文件,相当于 res.attachment() + res.sendFile()
res.cookie(name, value, [options]) 设置 Cookie(需要 cookie-parser 才能方便地读取)。
res.clearCookie(name, [options]) 清除指定 Cookie。
res.type(type) 设置 Content-Type 头,可接受 MIME 类型或扩展名(如 'html')。
res.format(object) 根据 Accept 请求头协商响应格式,调用对应的处理器。
res.append(field, value) 追加响应头(如果已存在,则用逗号分隔)。

2.2 链式调用

许多 res 方法返回 res 自身,因此可以链式调用:

res.status(201)
   .set('X-Powered-By', 'Express')
   .json({ id: 1, name: 'Alice' });

2.3 响应对象示例

app.get('/example', (req, res) => {
  // 发送纯文本
  res.send('Hello World');

  // 发送 JSON
  res.json({ message: 'Hello World' });

  // 设置状态码并发送文本
  res.status(404).send('Not Found');

  // 重定向
  res.redirect('/new-page');

  // 发送文件
  res.sendFile('/path/to/file.pdf');

  // 设置 Cookie
  res.cookie('username', 'john', { maxAge: 900000, httpOnly: true });
  res.send('Cookie set');

  // 协商响应格式
  res.format({
    'text/plain': () => { res.send('Plain text'); },
    'text/html': () => { res.send('

HTML

'); }, 'application/json': () => { res.json({ format: 'json' }); } }); });

3. 获取请求体 (req.body)

在 Express 中,默认情况下 req.bodyundefined。要解析请求体,必须使用中间件:

  • express.json():解析 application/json 格式的请求体。
  • express.urlencoded({ extended: true }):解析 application/x-www-form-urlencoded 格式的请求体(HTML 表单默认)。
  • express.raw():解析 application/octet-stream,将 body 作为 Buffer。
  • express.text():解析纯文本 body。
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.post('/login', (req, res) => {
  console.log(req.body); // { username: 'john', password: '123' }
  res.send('登录成功');
});

4. 完整示例:回显请求信息

以下是一个路由,它将客户端请求的方法、路径、参数、查询和头信息以 JSON 形式返回:

app.all('/echo', (req, res) => {
  res.json({
    method: req.method,
    url: req.url,
    path: req.path,
    query: req.query,
    params: req.params,
    headers: req.headers,
    ip: req.ip,
    hostname: req.hostname,
    body: req.body // 需要确保有解析中间件
  });
});

5. 注意事项

  • 每个请求只能调用一次 res.send()res.json() 等最终响应方法,多次调用会导致错误(后续调用被忽略或抛出异常)。
  • res.send()res.json() 会自动设置适当的 Content-Type,但也可以通过 res.type()res.set() 覆盖。
  • 如果未调用任何响应结束方法,请求会挂起直到超时,因此务必确保每个路由最终都调用一个终结方法。
  • 当使用 res.sendFile() 时,建议提供绝对路径,或使用 path.resolve() 构建安全路径。
总结: reqres 是 Express 应用的基石。掌握它们的属性和方法,能够灵活地获取客户端信息并构建各种响应。结合中间件和路由,你可以构建功能强大的 Web 应用和 API。