Node.js: Express 基础

最后更新:2026-08-26

1. 1 从原生 HTTP 到 Express

Alice 用原生 HTTP 模块写了 3 天的 API,代码又长又难维护——手动解析 URL、手写路由匹配、逐行处理请求体。改用 Express 后,同样的功能只用了 30 行代码,路由声明清晰、中间件自动串联,维护效率提升 10 倍。

原生 HTTP vs Express 代码对比

对比项 原生 HTTP Express
路由定义 手动 if/else 匹配 req.url 声明式 app.get('/path', fn)
请求体解析 手动监听 data/end 事件拼 Buffer express.json() 一行搞定
响应发送 res.writeHead() + res.end() res.json() / res.send()
静态文件 手动读文件 + 设 Content-Type express.static('public')
中间件 无内置支持 app.use() 管道式组合
代码量(同等功能) ~80 行 ~30 行


2. 2 安装与 Hello World

▶ 示例:(1) 初始化项目与安装 Express

BASH
mkdir my-express-app && cd my-express-app
npm init -y
npm install express

▶ 示例:(2) 最小 Hello World

JAVASCRIPT
const express = require('express');
const app = express();

app.get('/', (req, res) => {
  res.send('Hello World!');
});

app.listen(3000, () => {
  console.log('Server running at http://localhost:3000');
});
▶ 试一试
BASH
node app.js

浏览器访问 http://localhost:3000 即可看到 Hello World!

▶ 示例:返回 JSON 响应

JAVASCRIPT
app.get('/api/hello', (req, res) => {
  res.json({ message: 'Hello from Express!', status: 'ok' });
});
▶ 试一试

▶ 示例:带路由参数的 Hello

JAVASCRIPT
app.get('/hello/:name', (req, res) => {
  res.send(`Hello, ${req.params.name}!`);
});
▶ 试一试

3. 3 路由方法

Express 路由方法速查

方法 用途 幂等性 典型场景
app.get() 获取资源 查询列表/详情
app.post() 创建资源 提交表单/新增记录
app.put() 全量更新 替换整条记录
app.delete() 删除资源 删除记录
app.patch() 部分更新 修改个别字段
app.all() 匹配所有方法 通用预处理

▶ 示例:(1) RESTful 风格路由

JAVASCRIPT
app.get('/users', (req, res) => { res.json({ users: [] }); });
app.post('/users', (req, res) => { res.status(201).json({ created: true }); });
app.put('/users/:id', (req, res) => { res.json({ updated: req.params.id }); });
app.delete('/users/:id', (req, res) => { res.json({ deleted: req.params.id }); });
▶ 试一试

(2) 路由路径匹配

▶ 示例:查询参数读取

JAVASCRIPT
app.get('/search', (req, res) => {
  const { q, page = '1' } = req.query;
  res.json({ keyword: q, page: Number(page) });
});
▶ 试一试

▶ 示例:多路由参数

JAVASCRIPT
app.get('/posts/:postId/comments/:commentId', (req, res) => {
  res.json(req.params);
});
▶ 试一试

4. 4 中间件概念

Express 的核心设计是中间件管道——每个请求依次流过一系列函数,每个函数可以读取/修改请求与响应,或提前结束请求。

100%
flowchart LR
  A[请求 Request] --> B[中间件1<br/>日志记录]
  B --> C[中间件2<br/>JSON解析]
  C --> D[中间件3<br/>鉴权校验]
  D --> E[路由处理<br/>业务逻辑]
  E --> F[响应 Response]

▶ 示例:(1) app.use() 注册中间件

JAVASCRIPT
const logger = (req, res, next) => {
  console.log(`${req.method} ${req.url} - ${new Date().toISOString()}`);
  next();
};
app.use(logger);
▶ 试一试

(2) 中间件执行顺序

▶ 示例:鉴权中间件

JAVASCRIPT
const auth = (req, res, next) => {
  const token = req.headers['authorization'];
  if (!token) return res.status(401).json({ error: 'No token' });
  next();
};
app.use('/api', auth);
▶ 试一试

▶ 示例:错误处理中间件

JAVASCRIPT
app.use((err, req, res, next) => {
  console.error(err.stack);
  res.status(500).json({ error: 'Something went wrong!' });
});
▶ 试一试

5. 5 内置中间件

内置中间件列表

中间件 用途 引入方式 常见配置
express.json() 解析 JSON 请求体 app.use(express.json()) { limit: '10kb' }
express.urlencoded() 解析 URL 编码请求体 app.use(express.urlencoded({ extended: true })) { extended: true/false }
express.static() 托管静态文件 app.use(express.static('public')) { maxAge: '1d' }

▶ 示例:(1) express.json() 解析请求体

JAVASCRIPT
app.use(express.json());

app.post('/api/users', (req, res) => {
  console.log(req.body);
  res.json({ received: req.body });
});
▶ 试一试

(2) express.static() 托管静态文件

假设项目结构:

TEXT 📖 仅展示
my-express-app/
├── public/
│   ├── index.html
│   └── style.css
├── app.js
└── package.json
JAVASCRIPT
app.use(express.static('public'));

访问 http://localhost:3000/index.html 即可直接加载静态文件。

▶ 示例:多目录静态托管

JAVASCRIPT
app.use(express.static('public'));
app.use('/uploads', express.static('uploads'));
▶ 试一试

▶ 示例:限制 JSON 请求体大小

JAVASCRIPT
app.use(express.json({ limit: '100kb' }));
▶ 试一试

6. 6 nodemon 热重载

nodemon vs node 对比

对比项 node nodemon
文件变更后 手动重启 自动重启
安装方式 内置 npm i -D nodemon
启动命令 node app.js npx nodemon app.js
生产环境 适用 不适用
监听目录 默认当前目录,可配置

▶ 示例:(1) 安装与使用

BASH
npm install --save-dev nodemon
npx nodemon app.js

▶ 示例:(2) package.json 配置脚本

JAVASCRIPT
{
  "scripts": {
    "dev": "nodemon app.js",
    "start": "node app.js"
  }
}
▶ 试一试

开发时用 npm run dev(自动重载),生产用 npm start(稳定运行)。

▶ 示例:自定义监听目录

BASH
npx nodemon --watch src --ext js,ets app.js


7. 7 综合示例:Express API 应用

将前面所有知识点串联,从零搭建一个完整的小型 API 应用。

JAVASCRIPT
const express = require('express');
const app = express();

app.use(express.json());
app.use(express.static('public'));

let todos = [
  { id: 1, task: 'Learn Express', done: false },
  { id: 2, task: 'Build an API', done: false },
];

app.get('/api/todos', (req, res) => res.json(todos));

app.post('/api/todos', (req, res) => {
  const todo = { id: todos.length + 1, ...req.body, done: false };
  todos.push(todo);
  res.status(201).json(todo);
});

app.put('/api/todos/:id', (req, res) => {
  const idx = todos.findIndex(t => t.id === Number(req.params.id));
  if (idx === -1) return res.status(404).json({ error: 'Not found' });
  todos[idx] = { ...todos[idx], ...req.body };
  res.json(todos[idx]);
});

app.delete('/api/todos/:id', (req, res) => {
  todos = todos.filter(t => t.id !== Number(req.params.id));
  res.json({ deleted: true });
});

app.listen(3000, () => console.log('API running at http://localhost:3000'));

启动后即可用浏览器访问静态页面,用 API 工具操作 /api/todos 完成增删改查。


❓ 常见问题

Q Express 和 Koa 有什么区别?
A Express 基于回调中间件,生态丰富;Koa 基于 async/await,更轻量。新手选 Express 生态更成熟。
Q app.use 和 app.get 有什么区别?
A app.use 匹配所有请求方法,常用于中间件;app.get 只匹配 GET 请求,用于定义路由。
Q 中间件的执行顺序是什么?
A 按 app.use/app.get 注册顺序执行,next() 调用后进入下一个中间件,无 next() 则终止。
Q 如何处理 404?
A 在所有路由之后注册一个无路径的中间件,创建 404 错误并传递给错误处理中间件。
Q Express 能处理并发吗?
A Express 本身是单线程,但 Node.js 的事件循环使其能高效处理 I/O 并发,CPU 密集任务需用 Worker Threads。

Q: Express 是唯一选择吗? A: 不是,Koa 更轻量、Fastify 更快、Hono 支持边缘运行,但 Express 生态最成熟、学习资源最多,适合入门。

Q: express.json() 必须在路由前调用吗? A: 是的,app.use(express.json()) 必须写在路由之前,否则 req.bodyundefined,因为中间件按注册顺序执行。

Q: 4.x 和 5.x 有什么区别? A: Express 5.x 移除了已废弃 API(如 app.del),改进了路由匹配(支持 path-to-regexp v8),部分行为变为异步友好,但核心用法基本一致。

Q: 如何自动重启服务? A: 安装 nodemonnpm i -D nodemon),用 npx nodemon app.js 启动,文件保存后自动重启,开发时非常高效。

Q: app.use 和 app.get 有什么区别? A: app.use 匹配所有 HTTP 方法且路径前缀匹配(/api 匹配 /api/anything),app.get 仅匹配 GET 方法且路径精确匹配,前者用于中间件,后者用于路由。

Q: express.urlencoded 的 extended 选项什么意思? A: extended: true 使用 qs 库解析(支持嵌套对象),extended: false 使用 querystring 库(不支持嵌套),一般表单提交用 true 即可。


📖 小节


📝 作业

  1. 完成本课所有代码示例,确保每个示例都能正确运行
  2. 修改综合示例,添加自己的扩展功能
  3. 查阅官方文档,找出本课未涉及的1-2个API并编写测试代码
  4. 思考:在实际项目中,你会如何应用本课学到的知识?
  5. 尝试将本课知识与前面课程的内容结合,构建一个小项目
Web-Tutorial.com

Web-Tutorial 技术团队

由多位开发者共同维护的编程教程平台。每篇教程由对应领域的开发者编写和审核,确保内容准确可靠。如发现任何问题,欢迎向我们反馈。

100%

🙏 帮我们做得更好

我们是刚上线的编程教程站,几个人的小团队,精力有限。页面虽经检查,难免还有疏漏——链接失效、排版错乱、内容有误、语言生硬……

如果您发现了,麻烦告诉我们,我们会在收到反馈后第一时间进行修复,再次感谢您的光临 🙏