Express 框架入门:路由、中间件与静态文件
什么是 Express?
Express 是一个基于 Node.js 平台的极简、灵活的 Web 应用开发框架。它对 Node.js 的原生 HTTP 模块进行了高度封装,提供了一套简洁的 API,让你能够快速、优雅地构建 Web 应用程序和 API。本教程将带你掌握 Express 最核心的三大概念:路由、中间件与静态文件服务。
环境准备与安装
在开始之前,请确保你的系统已经安装了 Node.js(建议版本 14 及以上)。可以通过终端执行以下命令检查版本:
node -v
npm -v
创建项目目录并初始化:
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(`服务器运行在 http://localhost:${port}`);
});
运行 node app.js,在浏览器访问 http://localhost:3000,即可看到输出。下面我们深入路由、中间件和静态文件。
路由基础
路由决定了应用如何响应客户端对特定端点的请求。一个路由由一个 HTTP 方法、一个路径和一个处理函数构成。
基本路由
app.get('/hello', (req, res) => {
res.send('GET 请求到达 /hello');
});
app.post('/user', (req, res) => {
res.send('POST 请求创建用户');
});
app.put('/user/:id', (req, res) => {
res.send(`PUT 请求更新用户 ${req.params.id}`);
});
app.delete('/user/:id', (req, res) => {
res.send(`DELETE 请求删除用户 ${req.params.id}`);
});
路由参数与查询字符串
通过路径参数(:参数名)可以捕获 URL 中的动态值:
app.get('/products/:category/:id', (req, res) => {
const { category, id } = req.params;
res.send(`分类:${category},产品ID:${id}`);
});
访问 http://localhost:3000/products/electronics/123 将看到对应的输出。
查询字符串可以通过 req.query 获取:
app.get('/search', (req, res) => {
const { q, page } = req.query;
res.send(`搜索关键词:${q},页码:${page}`);
});
使用 express.Router 组织路由
当路由越来越多时,建议将其拆分为独立的路由模块。创建 routes/users.js:
const express = require('express');
const router = express.Router();
// 该路由挂载在 /users 下
router.get('/', (req, res) => {
res.send('用户列表');
});
router.get('/:id', (req, res) => {
res.send(`用户详情:${req.params.id}`);
});
module.exports = router;
在 app.js 中挂载:
const usersRouter = require('./routes/users');
app.use('/users', usersRouter);
中间件详解
中间件是 Express 最强大的特性之一。它是一个函数,可以访问请求对象(req)、响应对象(res)以及应用程序请求-响应循环中的下一个中间件函数(next)。
中间件执行顺序
Express 中间件按照代码定义的顺序依次执行。一个中间件可以执行任何代码、修改请求和响应对象、结束请求-响应循环,或者调用 next() 将控制权交给下一个中间件。
app.use((req, res, next) => {
console.log('第一个中间件');
next();
});
app.use((req, res, next) => {
console.log('第二个中间件');
res.send('中间件链结束');
});
内置中间件
Express 内置了几个常用的中间件函数。
- express.json():用于解析 JSON 格式的请求体。
- express.urlencoded({ extended: false }):用于解析 URL 编码的请求体(如表单提交)。
- express.static():提供静态文件服务(下一节详解)。
app.use(express.json());
app.use(express.urlencoded({ extended: false }));
这样配置后,req.body 就能正确获取客户端发送的数据了。
自定义中间件
你可以编写自己的中间件来实现日志记录、权限验证等功能。
const loggerMiddleware = (req, res, next) => {
const timestamp = new Date().toISOString();
console.log(`[${timestamp}] ${req.method} ${req.url}`);
next();
};
app.use(loggerMiddleware);
应用级中间件挂载在 app 上,也可以针对特定路由生效:
app.use('/admin', (req, res, next) => {
// 模拟权限检查
if (!req.headers.authorization) {
return res.status(403).send('未授权');
}
next();
});
错误处理中间件
错误处理中间件需要四个参数:(err, req, res, next)。它必须放在所有中间件和路由之后定义。
app.get('/error', (req, res) => {
throw new Error('故意抛出的错误');
});
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器内部错误!');
});
静态文件服务
Web 应用通常需要直接提供 HTML、CSS、JavaScript、图片等静态文件。Express 内置的 express.static 中间件让这一任务变得极其简单。
基本用法
假设将静态文件放在项目根目录下的 public 文件夹中:
my-express-app/
├── public/
│ ├── index.html
│ ├── style.css
│ └── images/
│ └── logo.png
└── app.js
在 app.js 中添加:
app.use(express.static('public'));
现在,直接访问 http://localhost:3000/index.html 或 http://localhost:3000/images/logo.png 即可获取对应文件。
虚拟路径前缀
有时我们希望静态文件挂载在一个虚拟路径下,例如所有静态资源都以 /static 开头访问:
app.use('/static', express.static('public'));
此时访问路径变为 http://localhost:3000/static/style.css。
多个静态目录
可以多次调用 express.static 来设置多个静态目录,Express 会按注册顺序查找文件。
app.use(express.static('public'));
app.use(express.static('uploads'));
配置选项
express.static 可以接受第二个参数来配置行为,例如设置缓存或索引文件:
app.use(express.static('public', {
maxAge: '1d', // 缓存一天
index: false, // 禁用目录索引
extensions: ['html'] // 当请求缺少扩展名时自动补全
}));
综合示例:构建一个简易 API 骨架
结合上述知识,我们来创建一个返回 JSON 数据的简易图书 API,同时为前端页面提供静态文件服务。
目录结构:
my-book-api/
├── public/
│ └── index.html
├── routes/
│ └── books.js
└── app.js
app.js:
const express = require('express');
const app = express();
const port = 3000;
// 内置中间件
app.use(express.json());
app.use(express.static('public'));
// 自定义日志中间件
app.use((req, res, next) => {
console.log(`${new Date().toLocaleTimeString()} - ${req.method} ${req.path}`);
next();
});
// 路由模块
const booksRouter = require('./routes/books');
app.use('/api/books', booksRouter);
// 错误处理中间件
app.use((err, req, res, next) => {
console.error(err.message);
res.status(500).json({ error: '服务器内部错误' });
});
app.listen(port, () => {
console.log(`服务已启动,访问 http://localhost:${port}`);
});
routes/books.js:
const express = require('express');
const router = express.Router();
let books = [
{ id: 1, title: 'Express入门', author: '张三' },
{ id: 2, title: 'Node.js实战', author: '李四' }
];
// 获取所有图书
router.get('/', (req, res) => {
res.json(books);
});
// 获取单本图书
router.get('/:id', (req, res) => {
const book = books.find(b => b.id === parseInt(req.params.id));
if (!book) return res.status(404).json({ error: '未找到该图书' });
res.json(book);
});
// 新增图书
router.post('/', (req, res) => {
const { title, author } = req.body;
if (!title || !author) {
return res.status(400).json({ error: '标题和作者不能为空' });
}
const newBook = {
id: books.length + 1,
title,
author
};
books.push(newBook);
res.status(201).json(newBook);
});
module.exports = router;
此时,你可以通过 http://localhost:3000/api/books 测试 API,并且在 public/index.html 中编写前端界面来消费这些接口。
总结
通过本教程,你已经掌握了 Express 框架的三大基石:
- 路由:定义应用端点,处理不同 HTTP 方法,组织模块化路由。
- 中间件:串联请求处理流程,实现日志、解析、鉴权等功能,并正确处理错误。
- 静态文件服务:轻松托管前端资源,支持虚拟路径和灵活配置。
Express 的生态系统极其丰富,接下来你可以继续学习模板引擎(如 EJS、Pug)、数据库集成(MongoDB/Mongoose、MySQL)以及部署相关的知识。开始构建你自己的 Web 应用吧!