4. Express 框架入门:Express 安装与路由、中间件机制、请求与响应处理
好,咱们进入正题。Express 是什么?说白了,它是 Node.js 生态里最流行的 Web 框架。我当年刚接触 Node 时,第一个学的就是它。为什么?因为它足够轻量,又足够强大。你想想看,一个框架能活十几年还这么火,肯定有它的道理。
4.1 安装 Express
安装 Express 非常简单。前提是你已经装好了 Node.js。我个人习惯用 npm 来管理依赖。
# 创建一个新项目
mkdir my-express-app
cd my-express-app
# 初始化 package.json
npm init -y
# 安装 Express
npm install express
嗯,这里要注意:npm init -y 会生成一个默认的 package.json。如果你想要更精细的控制,可以去掉 -y 手动填写。
装完之后,你的项目里会多出一个 node_modules 文件夹。别管它,那是依赖包。我们直接写代码。
4.2 第一个 Express 应用
创建一个 app.js 文件,写下这段代码:
const express = require('express');
const app = express();
const port = 3000;
app.get('/', (req, res) => {
res.send('Hello World!');
});
app.listen(port, () => {
console.log(`Server running at http://localhost:${port}`);
});
运行 node app.js,打开浏览器访问 http://localhost:3000。你会看到 "Hello World!"。就这么简单。
我在项目中遇到过不少新手,他们以为 Express 很复杂。其实核心就这几行代码。你想想看,一个 Web 服务器无非就是「接收请求 → 处理逻辑 → 返回响应」。Express 把这三步包装得特别优雅。
4.3 路由机制
路由,说白了就是「根据不同的 URL 和 HTTP 方法,执行不同的处理函数」。Express 的路由设计非常直观。
4.3.1 基本路由
// GET 请求
app.get('/users', (req, res) => {
res.send('获取用户列表');
});
// POST 请求
app.post('/users', (req, res) => {
res.send('创建新用户');
});
// PUT 请求
app.put('/users/:id', (req, res) => {
res.send(`更新用户 ${req.params.id}`);
});
// DELETE 请求
app.delete('/users/:id', (req, res) => {
res.send(`删除用户 ${req.params.id}`);
});
看到 :id 了吗?这是路由参数。Express 会自动解析 URL 中的动态部分,放到 req.params 里。我曾经踩过一个坑:路由参数的命名不要用连字符,比如 :user-id 在某些版本里会有问题。建议用下划线::user_id。
4.3.2 路由分组
当路由多了以后,都堆在 app 上会很难维护。Express 提供了 Router 来分组:
const router = express.Router();
router.get('/', (req, res) => {
res.send('首页');
});
router.get('/about', (req, res) => {
res.send('关于我们');
});
// 挂载到 /site 路径下
app.use('/site', router);
这样,访问 /site/ 和 /site/about 就会分别触发对应的处理函数。我个人习惯把每个模块的路由单独放在一个文件里,比如 routes/users.js、routes/products.js。项目大了以后,这种组织方式会让你少掉很多头发。
4.4 中间件机制
中间件是 Express 最核心的概念。没有之一。你想想看,一个请求从进入服务器到返回响应,中间要经过多少步骤?日志记录、身份验证、参数解析、错误处理……这些步骤如果都写在路由处理函数里,代码会变得又臭又长。
中间件就是用来解决这个问题的。它是一个函数,可以访问请求对象、响应对象,以及下一个中间件函数。
4.4.1 中间件的结构
function myMiddleware(req, res, next) {
// 做一些处理
console.log('中间件执行了');
// 调用下一个中间件
next();
}
// 使用中间件
app.use(myMiddleware);
注意:一定要调用 next(),否则请求会卡住。我曾经调试一个 bug 调了半小时,最后发现就是忘了写 next()。嗯,这种错误犯过一次就不会再犯了。
4.4.2 常用内置中间件
Express 自带了一些中间件,我列几个最常用的:
| 中间件 | 作用 | 使用方式 |
|---|---|---|
| express.json() | 解析 JSON 请求体 | app.use(express.json()) |
| express.urlencoded() | 解析 URL 编码的请求体 | app.use(express.urlencoded({ extended: true })) |
| express.static() | 提供静态文件服务 | app.use(express.static('public')) |
举个例子,如果你要处理表单提交,必须加上 express.urlencoded()。否则 req.body 会是 undefined。我见过太多人在这上面栽跟头了。
4.4.3 自定义中间件实战
咱们写一个日志中间件,记录每个请求的方法和 URL:
const logger = (req, res, next) => {
const start = Date.now();
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
// 监听响应结束事件
res.on('finish', () => {
const duration = Date.now() - start;
console.log(` 响应状态: ${res.statusCode},耗时: ${duration}ms`);
});
next();
};
app.use(logger);
这个中间件会在每个请求进来时打印日志,并在响应结束时打印耗时。你想想看,如果每个路由都手动加这些代码,那得多痛苦。中间件就是用来干这种「横切关注点」的事。
4.5 请求与响应处理
这部分是实际开发中打交道最多的。咱们一个一个说。
4.5.1 请求对象 (req)
req 对象包含了客户端发来的所有信息。常用的属性有:
req.params- 路由参数,比如/users/:id中的idreq.query- URL 查询参数,比如?name=john&age=25req.body- 请求体,需要配合中间件解析req.headers- 请求头req.method- HTTP 方法req.path- 请求路径
app.get('/search', (req, res) => {
const { q, page = 1 } = req.query;
res.send(`搜索关键词: ${q},页码: ${page}`);
});
app.post('/users', (req, res) => {
const { name, email } = req.body;
res.send(`创建用户: ${name} (${email})`);
});
4.5.2 响应对象 (res)
res 对象用来给客户端返回数据。常用的方法:
res.send()- 发送各种类型的数据(字符串、Buffer、对象、数组)res.json()- 发送 JSON 格式数据res.status()- 设置 HTTP 状态码res.redirect()- 重定向res.render()- 渲染模板(需要配置模板引擎)
app.get('/api/user', (req, res) => {
const user = {
id: 1,
name: '张三',
email: 'zhangsan@example.com'
};
// 设置状态码并返回 JSON
res.status(200).json({
success: true,
data: user
});
});
app.get('/old-page', (req, res) => {
// 重定向到新页面
res.redirect(301, '/new-page');
});
{ success: true/false, data: ..., message: ... } 这种格式。前端处理起来特别方便,后端也容易维护。
4.6 错误处理中间件
最后聊一个重要的东西:错误处理。Express 有专门的错误处理中间件,它有四个参数:
app.use((err, req, res, next) => {
console.error('发生错误:', err.stack);
res.status(err.status || 500).json({
success: false,
message: err.message || '服务器内部错误'
});
});
注意:错误处理中间件必须放在所有路由和中间件之后。而且它必须有四个参数,少一个都不行。我曾经因为少写了 next 参数,导致错误处理不生效,查了半天才发现。
async/await,一定要用 try/catch 捕获错误,或者使用 express-async-errors 这样的库。否则未捕获的 Promise 异常会导致服务器崩溃。
好了,Express 的基础就这些。你想想看,从安装到路由,从中间件到请求响应,其实核心概念并不多。但就是这些基础的东西,组合起来能构建出非常强大的 API。下一章咱们会深入 GraphQL 与 Express 的集成,到时候你会看到这些知识是如何串联起来的。