Express 是一个基于 Node.js 平台的轻量级、灵活的 Web 应用框架,它为构建 Web 应用和 API 提供了一系列强大的功能。
核心特性
-
中间件支持:Express 使用中间件(middleware)函数来处理 HTTP 请求和响应。中间件可以访问请求对象(req)、响应对象(res),以及应用程序的请求-响应循环中的下一个中间件函数。通过中间件,你可以执行各种任务,如日志记录、解析请求体、路由处理等。
-
路由:Express 提供了简洁而灵活的路由机制,允许你根据不同的 HTTP 方法(GET, POST 等)和 URL 路径定义处理逻辑。你可以创建复杂的路由结构,并且支持动态路由参数。
-
视图系统:Express 支持多种模板引擎,如 EJS、Pug、Handlebars 等,使得你可以轻松地生成 HTML 页面。
-
错误处理:内置错误处理机制,可以通过特定的中间件来捕获并处理应用中的错误。
-
与数据库集成:虽然 Express 本身不绑定任何数据库,但其灵活性意味着它可以轻松地与 MongoDB、MySQL、PostgreSQL 等数据库集成。
快速入门
安装 Express:
npm install express --save
创建一个简单的服务器:
const express = require('express');
const app = express();app.get('/', (req, res) => {res.send('Hello World!');
});app.listen(3000, () => {console.log('Server is running on port 3000.');
});
Express兼容原生http响应
Express 中,响应对象(res)是对 Node.js 原生 http.ServerResponse 对象的扩展,因此它兼容原生 HTTP 的响应方法。这意味着你可以使用原生的方法,也可以使用由 Express 提供的更高级的 API 来发送响应。
//原生 Node.js:
//使用 res.statusCode、res.setHeader、res.writeconst express = require('express');
const http = require('http');const app = express();// Express 中间件处理
app.use((req, res, next) => {// 原生 HTTP 响应方法示例res.statusCode = 200;res.setHeader('Content-Type', 'text/plain');res.write('Hello from Express with native HTTP!');// 继续 Express 流程next();
});// Express 路由处理
app.get('/', (req, res) => {// 可以继续使用 Express APIres.end(' (with Express finish)');
});app.listen(3000, () => {console.log('Server running on port 3000');
});
原生 Node.js: 需要手动设置状态码为 3xx 并设置 Location
头。
response.writeHead(302, {Location: 'http://example.com'});
response.end();
Express: 使用便捷的 .redirect()
方法。
res.redirect('http://example.com');
Express 提供了便捷的方法如 .download()
和直接使用流(Stream)来响应文件下载请求。
这是 Express 特有的方法,用于触发文件下载,并自动设置适当的响应头(如 Content-Disposition: attachment
)。
const express = require('express');
const app = express();
const path = require('path');app.get('/download', (req, res) => {const filePath = path.join(__dirname, 'public', 'example.txt');res.download(filePath); // 自动触发下载
});
参数说明:
res.download(filePath, [filename], [options], [callback])
filePath
: 文件在服务器上的路径。[filename]
: 客户端看到的文件名(可选,默认为原始文件名)。[options]
: 可选配置对象,例如{ headers: { ... } }
。[callback]
: 下载完成后的回调函数。
原生 http
的方法时,你需要手动控制所有流程,包括读取文件、设置响应头、发送数据等
// const http = require('http');
const fs = require('fs');
const path = require('path');
const express = require('express');const app = express();// const server = http.createServer((req, res) => {
// if (req.url === '/download') {
// const filePath = path.join(__dirname, 'example.txt');// res.writeHead(200, {
// 'Content-Type': 'application/octet-stream',
// 'Content-Disposition': 'attachment; filename="example.txt"'
// });// const readStream = fs.createReadStream(filePath);
// readStream.pipe(res);
// } else {
// res.writeHead(404);
// res.end('Not Found');
// }
// });// server.listen(3000, () => {
// console.log('Server running on port 3000');
// });app.get('/download', (req, res) => {const filePath = path.join(__dirname, 'example.txt');res.writeHead(200, {'Content-Type': 'application/octet-stream','Content-Disposition': 'attachment; filename="example.txt"'});const readStream = fs.createReadStream(filePath);readStream.pipe(res);
});app.listen(3000, () => {console.log('Server running on port 3000');});
express 特有的响应客户端方法
res.send()
自动根据传入数据类型设置合适的响应头,并发送响应体。
- 如果是字符串:默认设置
Content-Type: text/html
- 如果是对象或数组:自动调用
JSON.stringify()
,并设置Content-Type: application/json
- 支持 Buffer 数据(如二进制)
res.send('Hello World'); // 发送 HTML 或纯文本
res.send({ name: 'Tom' }); // 自动转为 JSON 并设置 Content-Type: application/json
res.send(Buffer.from('ABC')); // 发送二进制数据
- 智能处理不同类型的数据。
- 不会触发浏览器下载行为。
- 适合返回 API 响应、HTML 页面等。
res.json()
专门用于发送 JSON 格式的数据,自动调用 JSON.stringify()
并设置 Content-Type: application/json
。
res.json({ success: true, data: { id: 1 } });
// 输出:
// {"success":true,"data":{"id":1}}
-
保证响应内容为标准 JSON 格式。
- 设置正确的
Content-Type
。 - 推荐用于构建 RESTful API。
⚠️ 注意:如果传入的是一个循环引用的对象,
json()
会抛出错误。
res.sendFile()
用于发送一个文件给客户端(例如 HTML 文件、图片等),常用于静态资源服务。
const path = require('path');app.get('/', (req, res) => {res.sendFile(path.join(__dirname, 'public', 'index.html'));
});
-
必须传入文件的绝对路径(推荐使用
path.join()
构造)。 - 自动设置适当的 MIME 类型(如
text/html
,image/png
等)。 - 不会强制浏览器下载,而是尝试在浏览器中直接显示(如 PDF、图片等)。
res.status(code)
- 用途:设置 HTTP 状态码。通常与其他响应方法链式使用。
-
res.status(404).send('Not Found');
res.set(field [, value])
或 res.header(field [, value])
- 用途:设置 HTTP 响应头。
-
res.set('Content-Type', 'text/plain'); // 或者 res.set({'Content-Type': 'text/plain','ETag': '12345' });
res.type(type)
或 res.contentType(type)
- 用途:设置 Content-Type 响应头。
type
参数可以是 MIME 类型或文件扩展名。 -
res.type('.html'); // Content-Type 设置为 text/html res.type('json'); // Content-Type 设置为 application/json
路由(Routing)
处理不同路径和 HTTP 方法的请求。
基本路由
// GET 请求
app.get('/users', (req, res) => { /* ... */ });// POST 请求
app.post('/users', (req, res) => { /* ... */ });// 动态路由参数
app.get('/users/:id', (req, res) => {const userId = req.params.id;res.send(`User ${userId}`);
});
路由分组
使用 express.Router
创建模块化路由:
// routes/users.js
const router = require('express').Router();router.get('/', (req, res) => { /* 获取所有用户 */ });
router.post('/', (req, res) => { /* 创建用户 */ });module.exports = router;// main.js
const userRoutes = require('./routes/users');
app.use('/api/users', userRoutes);
中间件(Middleware)
Express 中,中间件是处理 HTTP 请求的核心机制,它允许你在请求到达路由处理函数之前或之后执行代码。中间件函数可以:
- 修改请求和响应对象(如添加属性、设置头部)
- 结束请求 - 响应循环(如返回错误或结果)
- 调用下一个中间件函数(通过
next()
)
内置中间件
// 解析 JSON 请求体
app.use(express.json());// 静态文件服务
app.use(express.static('public'));//有时你可能希望为静态文件添加一个路径前缀,可以通过将挂载点作为第一个参数传递给 express.static 实现:
app.use('/static', express.static('public'));
// 现在,你需要通过 /static/example.html 来访问 public 目录下的 example.html 文件//默认情况下,如果 express.static 找不到请求的文件,它不会发送下一个中间件。如果你想改变这种行为,可以让它继续执行后续的中间件:app.use(express.static('public', { fallthrough: true }));app.use((req, res, next) => {res.status(404).send('Sorry, we cannot find that!');
});
// 这将确保当静态文件未找到时,仍然有机会返回自定义的 404 页面或其他内容。
自定义中间件
中间件函数接收三个参数:(req, res, next)
req
:HTTP 请求对象res
:HTTP 响应对象next
:指向下一个中间件的函数,必须调用它才能继续执行后续中间件
// 日志中间件
app.use((req, res, next) => {console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);next(); // 传递控制权给下一个中间件
});// 错误处理中间件(必须有 4 个参数)
app.use((err, req, res, next) => {console.error(err);res.status(500).send('Internal Server Error');
});
中间件的类型
1. 应用级中间件
绑定到 app
实例,处理所有路由请求:
// 记录所有请求的时间戳
app.use((req, res, next) => {req.requestTime = Date.now();next();
});
2. 路由级中间件
绑定到特定路由,只处理匹配的请求:
// 仅处理 /users 路径的请求
app.use('/users', (req, res, next) => {console.log('Processing user-related request');next();
});
3. 错误处理中间件
接收四个参数 (err, req, res, next)
,用于捕获和处理错误:
app.use((err, req, res, next) => {console.error(err.stack);res.status(500).send('Internal Server Error');
});
错误处理
错误处理中间件与普通中间件类似,但需要四个参数,其签名如下:(err, req, res, next)。即使你不使用 next 参数,也必须指定它,以便 Express 能够识别这是一个错误处理中间件。
统一处理应用中的异常:
app.get('/users',(req, res) => {try {const data = getData();req.data = data;next();} catch (err) {next(err); // 将错误传递给错误处理中间件}
});// 全局错误处理
app.use((err, req, res, next) => {const statusCode = err.statusCode || 500;res.status(statusCode).json({error: {message: err.message,status: statusCode}});
});
关键点解释
- 触发错误:在任何地方调用
next()
并传入一个错误对象,就可以将控制权交给第一个匹配的错误处理中间件。 - 错误处理中间件:使用
app.use()
来定义,它会捕捉所有通过next(err)
传递过来的错误。 - 环境变量判断:在这个例子中,根据应用程序环境(
development
或production
),决定是否向客户端暴露详细的错误信息。这有助于保护生产环境下的敏感信息泄露。