Express 是 Node.js 最流行的 Web 框架,它提供了简洁的 API 用于构建 Web 应用和 API。本章将带你从零开始学习 Express,包括路由、中间件、静态文件处理等核心概念,并创建一个简单的 REST API 示例。
Express 是一个基于 Node.js 平台的极简、灵活的 Web 应用开发框架,它提供了一系列强大的特性,帮助开发者快速创建 Web 服务器和 API。Express 的核心特性包括:
许多流行的 Node.js 框架(如 NestJS、Sails)都是基于 Express 构建的。
首先创建一个新项目并初始化 package.json:
mkdir my-express-app
cd my-express-app
npm init -y
然后安装 Express:
npm install express
安装完成后,就可以开始编写第一个 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() 创建一个 Express 应用实例。app.get() 定义了一个路由,处理对根路径 / 的 GET 请求。res.send() 发送响应内容。app.listen() 启动服务器并监听指定端口。路由用于确定应用程序如何响应客户端对特定端点的请求。每个路由可以有一个或多个处理函数。
// 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}`);
});
通过 req.params 可以获取 URL 中的动态参数:
app.get('/users/:userId/books/:bookId', (req, res) => {
res.send(req.params); // 例如 { userId: '123', bookId: '456' }
});
通过 req.query 获取 URL 中的查询参数:
app.get('/search', (req, res) => {
res.send(req.query); // 例如 /search?q=express&page=2 => { q: 'express', page: '2' }
});
Express 扩展了 Node.js 的原生请求和响应对象,提供了许多便捷方法。
req.params:路由参数对象。req.query:查询字符串对象。req.body:请求体(需要中间件解析)。req.headers:请求头。req.method:HTTP 方法。req.path:请求路径。res.send():发送响应(可以是字符串、对象、Buffer)。res.json():发送 JSON 响应(自动设置 Content-Type)。res.status():设置 HTTP 状态码,可链式调用。res.redirect():重定向到指定 URL。res.render():渲染模板引擎的视图。res.sendFile():发送文件。app.get('/json', (req, res) => {
res.json({ message: 'Hello JSON' });
});
app.get('/status', (req, res) => {
res.status(201).send('Created');
});
中间件是 Express 的核心概念。它是一个函数,可以访问请求对象 (req)、响应对象 (res) 和应用程序的请求-响应循环中的下一个中间件函数 (next)。中间件可以:
如果当前中间件没有结束请求-响应循环,必须调用 next() 将控制权传递给下一个中间件,否则请求会挂起。
使用 app.use() 或 app.METHOD() 挂载中间件。
// 记录请求日志的中间件
app.use((req, res, next) => {
console.log(`${req.method} ${req.url}`);
next(); // 必须调用
});
// 只对特定路径生效的中间件
app.use('/admin', (req, res, next) => {
console.log('Admin 请求');
next();
});
路由级中间件与应用级中间件用法相同,但绑定到 express.Router() 实例上。详见下文路由模块化。
错误处理中间件有四个参数 (err, req, res, next),必须放在所有其他中间件之后。
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('Something broke!');
});
Express 内置了一些常用中间件:
express.json():解析 JSON 请求体。express.urlencoded({ extended: true }):解析 URL-encoded 请求体。express.static():托管静态文件。app.use(express.json()); // 支持 application/json
app.use(express.urlencoded({ extended: true })); // 支持 application/x-www-form-urlencoded
为了获取 POST 或 PUT 请求中的请求体数据,需要使用中间件解析。例如:
app.use(express.json());
app.post('/api/users', (req, res) => {
console.log(req.body); // 从客户端发送的 JSON 数据
res.json({ received: req.body });
});
使用 express.static 托管静态文件,例如图片、CSS 和 JavaScript 文件。
app.use(express.static('public'));
现在,你可以通过 http://localhost:3000/images/logo.png 访问 public/images/logo.png。
也可以为静态文件指定虚拟路径前缀:
app.use('/static', express.static('public'));
那么访问 URL 变为 http://localhost:3000/static/images/logo.png。
使用 express.Router 创建模块化、可挂载的路由处理程序。例如,创建一个 routes/users.js:
const express = require('express');
const router = express.Router();
// 该路由中间件只在此路由中生效
router.use((req, res, next) => {
console.log('用户路由中间件');
next();
});
router.get('/', (req, res) => {
res.send('用户列表');
});
router.post('/', (req, res) => {
res.send('创建用户');
});
router.get('/:id', (req, res) => {
res.send(`用户详情: ${req.params.id}`);
});
module.exports = router;
然后在主应用中挂载:
const userRouter = require('./routes/users');
app.use('/users', userRouter);
下面是一个完整的 REST API 示例,使用内存数组存储用户数据:
const express = require('express');
const app = express();
app.use(express.json());
let users = [
{ id: 1, name: 'Alice' },
{ id: 2, name: 'Bob' }
];
// 获取所有用户
app.get('/api/users', (req, res) => {
res.json(users);
});
// 获取单个用户
app.get('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) return res.status(404).json({ error: 'User not found' });
res.json(user);
});
// 创建新用户
app.post('/api/users', (req, res) => {
const newUser = {
id: users.length + 1,
name: req.body.name
};
users.push(newUser);
res.status(201).json(newUser);
});
// 更新用户
app.put('/api/users/:id', (req, res) => {
const user = users.find(u => u.id === parseInt(req.params.id));
if (!user) return res.status(404).json({ error: 'User not found' });
user.name = req.body.name;
res.json(user);
});
// 删除用户
app.delete('/api/users/:id', (req, res) => {
const index = users.findIndex(u => u.id === parseInt(req.params.id));
if (index === -1) return res.status(404).json({ error: 'User not found' });
users.splice(index, 1);
res.status(204).send(); // 无内容
});
const port = 3000;
app.listen(port, () => console.log(`Server running on port ${port}`));
Express 支持多种模板引擎,如 EJS、Pug。以 EJS 为例:
npm install ejs
设置模板引擎和视图目录:
app.set('view engine', 'ejs');
app.set('views', './views');
创建一个 views/index.ejs 文件:
<h1><%= title %></h1>
<p>Welcome to Express</p>
在路由中渲染:
app.get('/', (req, res) => {
res.render('index', { title: 'Express App' });
});
定义错误处理中间件(通常放在所有路由之后):
// 捕获 404 并转发到错误处理器
app.use((req, res, next) => {
res.status(404).json({ error: 'Not Found' });
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).json({ error: 'Internal Server Error' });
});
nodemon 监听文件变化自动重启服务器。process.env.PORT 获取端口,便于部署。npm install morgan,然后 app.use(require('morgan')('dev'));。npm install cors,然后 app.use(require('cors')());。