Node.js Express 框架开发实战指南
前言
Express 是基于 Node.js 平台,快速、开放、极简的 Web 开发框架。作为 Node.js 最流行的 Web 框架,Express 提供了一套简洁而灵活的 API,可以帮助开发者快速构建各种 Web 应用和移动应用的后端。本文将全面介绍 Express 框架的核心概念、路由设计、中间件使用和项目最佳实践。
基础入门
环境准备
在开始之前,请确保已安装 Node.js(建议使用 LTS 版本)和 npm。
# 检查 Node.js 和 npm 版本
node -v
npm -v
# 创建项目目录
mkdir express-demo
cd express-demo
# 初始化项目
npm init -y
# 安装 Express
npm install express
创建第一个应用
创建一个名为 app.js
的文件,包含基本的 Express 应用:
// 引入 Express 模块
const express = require('express');
// 创建 Express 应用
const app = express();
const port = 3000;
// 定义根路由
app.get('/', (req, res) => {
res.send('Hello World!');
});
// 启动服务器
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});
运行应用并在浏览器中访问 http://localhost:3000
:
node app.js
路由系统
基本路由
Express 的路由定义采用以下结构:
app.METHOD(PATH, HANDLER);
METHOD
是 HTTP 方法(如 get, post, put, delete 等)PATH
是服务器上的路径HANDLER
是当路由匹配时执行的函数
// 基本路由示例
app.get('/', (req, res) => {
res.send('首页');
});
app.post('/users', (req, res) => {
res.send('创建用户');
});
app.put('/users/:id', (req, res) => {
res.send(`更新用户 ${req.params.id}`);
});
app.delete('/users/:id', (req, res) => {
res.send(`删除用户 ${req.params.id}`);
});
路由参数
可以通过 req.params
对象访问路由参数:
// 路由参数示例
app.get('/users/:userId/books/:bookId', (req, res) => {
res.send(`用户 ${req.params.userId} 的图书 ${req.params.bookId}`);
});
路由处理器
可以为一个路由提供多个回调函数:
// 多个回调函数
app.get('/example',
(req, res, next) => {
console.log('第一个回调');
next(); // 传递控制权给下一个回调
},
(req, res) => {
res.send('第二个回调');
}
);
路由模块化
随着应用规模扩大,应将路由分离到独立模块:
// routes/users.js
const express = require('express');
const router = express.Router();
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);
中间件
中间件函数可以访问请求对象、响应对象和应用程序的请求-响应周期中的 next
函数。
应用级中间件
// 所有请求都会经过此中间件
app.use((req, res, next) => {
console.log('Time:', Date.now());
next();
});
// 特定路径的中间件
app.use('/users', (req, res, next) => {
console.log('Request Type:', req.method);
next();
});
常用内置中间件
// 解析 JSON 请求体
app.use(express.json());
// 解析 URL 编码的请求体
app.use(express.urlencoded({ extended: true }));
// 静态文件服务
app.use(express.static('public'));
错误处理中间件
错误处理中间件必须有四个参数:
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器错误');
});
第三方中间件
Express 生态系统中有许多有用的第三方中间件:
// 安装第三方中间件
npm install morgan cors helmet
// 使用举例
const morgan = require('morgan');
const cors = require('cors');
const helmet = require('helmet');
app.use(morgan('dev')); // 请求日志
app.use(cors()); // 处理跨域
app.use(helmet()); // 安全相关 HTTP 头
请求和响应
Express 扩展了 Node.js 的 req
和 res
对象,提供了额外的方法和属性。
请求对象(req)
// 查询字符串参数
app.get('/search', (req, res) => {
console.log(req.query.term); // 获取查询参数 ?term=value
res.send(`搜索: ${req.query.term}`);
});
// 请求体
app.post('/data', (req, res) => {
console.log(req.body); // 已通过 express.json() 或 express.urlencoded() 解析
res.send('数据已接收');
});
// 请求头
app.get('/headers', (req, res) => {
console.log(req.get('Content-Type')); // 获取请求头
res.send('请求头已处理');
});
响应对象(res)
// 发送响应
res.send('文本响应');
res.send({ user: 'John' }); // 自动转换为 JSON
res.json({ user: 'John' }); // 明确发送 JSON
// 设置状态码
res.status(201).send('资源已创建');
res.status(404).send('未找到资源');
// 重定向
res.redirect('/home');
res.redirect(301, '/new-page'); // 301 永久重定向
// 设置响应头
res.set('Content-Type', 'text/plain');
res.set({
'Content-Type': 'text/plain',
'X-Custom-Header': 'Custom Value'
});
// 渲染模板 (需要设置模板引擎)
res.render('index', { title: '首页', user: 'John' });
视图和模板引擎
Express 支持多种模板引擎,如 Pug, EJS, Handlebars 等。
// 安装 EJS 模板引擎
npm install ejs
// 配置模板引擎
app.set('view engine', 'ejs');
app.set('views', './views'); // 模板文件目录
// 创建视图 (views/index.ejs)
/*
<!DOCTYPE html>
<html>
<head>
<title><%= title %></title>
</head>
<body>
<h1>Hello, <%= name %>!</h1>
</body>
</html>
*/
// 渲染视图
app.get('/', (req, res) => {
res.render('index', { title: 'Express', name: 'World' });
});
数据库集成
Express 可以与多种数据库集成,包括 MongoDB, MySQL, PostgreSQL 等。
MongoDB 示例 (使用 Mongoose)
// 安装 Mongoose
npm install mongoose
// 连接到 MongoDB
const mongoose = require('mongoose');
mongoose.connect('mongodb://localhost:27017/myapp', {
useNewUrlParser: true,
useUnifiedTopology: true
});
// 定义用户模型
const userSchema = new mongoose.Schema({
name: String,
email: { type: String, required: true, unique: true },
password: { type: String, required: true },
createdAt: { type: Date, default: Date.now }
});
const User = mongoose.model('User', userSchema);
// 创建用户
app.post('/users', async (req, res) => {
try {
const user = new User(req.body);
await user.save();
res.status(201).send(user);
} catch (error) {
res.status(400).send(error);
}
});
// 获取所有用户
app.get('/users', async (req, res) => {
try {
const users = await User.find({});
res.send(users);
} catch (error) {
res.status(500).send(error);
}
});
SQL 数据库示例 (使用 Sequelize)
// 安装 Sequelize 和 MySQL 驱动
npm install sequelize mysql2
// 连接到 MySQL
const { Sequelize, DataTypes } = require('sequelize');
const sequelize = new Sequelize('database', 'username', 'password', {
host: 'localhost',
dialect: 'mysql'
});
// 定义用户模型
const User = sequelize.define('User', {
name: DataTypes.STRING,
email: {
type: DataTypes.STRING,
allowNull: false,
unique: true
},
password: {
type: DataTypes.STRING,
allowNull: false
}
});
// 同步模型到数据库
(async () => {
await sequelize.sync();
console.log('数据库同步完成');
})();
// 创建用户
app.post('/users', async (req, res) => {
try {
const user = await User.create(req.body);
res.status(201).send(user);
} catch (error) {
res.status(400).send(error);
}
});
// 获取所有用户
app.get('/users', async (req, res) => {
try {
const users = await User.findAll();
res.send(users);
} catch (error) {
res.status(500).send(error);
}
});
身份验证与授权
使用 JWT (JSON Web Tokens) 实现身份验证:
// 安装依赖
npm install jsonwebtoken bcryptjs
// 用户登录
const jwt = require('jsonwebtoken');
const bcrypt = require('bcryptjs');
app.post('/login', async (req, res) => {
try {
// 查找用户
const user = await User.findOne({ email: req.body.email });
if (!user) return res.status(400).send('用户不存在');
// 验证密码
const validPassword = await bcrypt.compare(req.body.password, user.password);
if (!validPassword) return res.status(400).send('密码错误');
// 生成 token
const token = jwt.sign({ id: user._id }, 'your_jwt_secret', { expiresIn: '2h' });
res.send({ token });
} catch (error) {
res.status(500).send(error);
}
});
// 验证令牌的中间件
function auth(req, res, next) {
const token = req.header('x-auth-token');
if (!token) return res.status(401).send('未提供令牌');
try {
const verified = jwt.verify(token, 'your_jwt_secret');
req.user = verified;
next();
} catch (error) {
res.status(400).send('无效的令牌');
}
}
// 受保护的路由
app.get('/protected', auth, (req, res) => {
res.send('这是受保护的资源');
});
文件上传
使用 multer 中间件处理文件上传:
// 安装 multer
npm install multer
// 配置文件上传
const multer = require('multer');
const path = require('path');
const storage = multer.diskStorage({
destination: (req, file, cb) => {
cb(null, 'uploads/');
},
filename: (req, file, cb) => {
cb(null, `${Date.now()}-${file.originalname}`);
}
});
const upload = multer({
storage,
limits: { fileSize: 1000000 }, // 限制 1MB
fileFilter: (req, file, cb) => {
// 检查文件类型
const filetypes = /jpeg|jpg|png|gif/;
const extname = filetypes.test(path.extname(file.originalname).toLowerCase());
const mimetype = filetypes.test(file.mimetype);
if (mimetype && extname) {
return cb(null, true);
} else {
cb('错误:仅支持图片文件');
}
}
});
// 单文件上传
app.post('/upload', upload.single('image'), (req, res) => {
res.send(`文件上传成功: ${req.file.path}`);
});
// 多文件上传
app.post('/upload-multiple', upload.array('images', 5), (req, res) => {
res.send(`上传了 ${req.files.length} 个文件`);
});
API 设计最佳实践
RESTful API 设计
// 资源命名 - 使用复数名词
app.get('/api/users', ...); // 获取所有用户
app.get('/api/users/:id', ...); // 获取特定用户
app.post('/api/users', ...); // 创建用户
app.put('/api/users/:id', ...); // 更新用户
app.delete('/api/users/:id', ...); // 删除用户
// 嵌套资源
app.get('/api/users/:userId/posts', ...); // 获取用户的所有帖子
app.get('/api/users/:userId/posts/:postId', ...); // 获取用户的特定帖子
错误处理
// 处理异步错误
function asyncHandler(fn) {
return (req, res, next) => {
Promise.resolve(fn(req, res, next)).catch(next);
};
}
app.get('/users', asyncHandler(async (req, res) => {
const users = await User.find({});
res.send(users);
}));
// 集中式错误处理
app.use((err, req, res, next) => {
// 记录错误
console.error(err.stack);
// 根据环境返回不同级别的错误信息
if (process.env.NODE_ENV === 'production') {
res.status(500).send('服务器错误');
} else {
res.status(500).send(`错误: ${err.message}\n${err.stack}`);
}
});
版本控制
// 使用 URL 路径版本化
app.use('/api/v1', v1Router);
app.use('/api/v2', v2Router);
// 或使用请求头版本化
app.use((req, res, next) => {
const version = req.get('Accept-Version') || 'v1';
req.apiVersion = version;
next();
});
性能优化
压缩响应
// 安装 compression
npm install compression
// 启用压缩
const compression = require('compression');
app.use(compression());
缓存控制
// 设置缓存头
app.get('/api/data', (req, res) => {
res.set('Cache-Control', 'public, max-age=300'); // 缓存5分钟
res.send(data);
});
负载测试
# 使用 autocannon 进行负载测试
npm install -g autocannon
autocannon -c 100 -d 5 http://localhost:3000/api/users
部署与生产环境配置
环境变量管理
// 安装 dotenv
npm install dotenv
// 加载环境变量
require('dotenv').config();
const port = process.env.PORT || 3000;
const dbUrl = process.env.DATABASE_URL;
app.listen(port, () => {
console.log(`服务器运行在端口 ${port}`);
});
进程管理
# 安装 PM2
npm install -g pm2
# 启动应用
pm2 start app.js --name "my-api" -i max
# 监控
pm2 monit
# 日志查看
pm2 logs
安全最佳实践
// 安装安全相关中间件
npm install helmet cors rate-limiter-flexible
// 配置安全中间件
const helmet = require('helmet');
const cors = require('cors');
const { RateLimiterMemory } = require('rate-limiter-flexible');
// 使用 Helmet 设置安全相关 HTTP 头
app.use(helmet());
// 配置 CORS
app.use(cors({
origin: ['https://yourapp.com', 'https://www.yourapp.com'],
methods: ['GET', 'POST', 'PUT', 'DELETE'],
allowedHeaders: ['Content-Type', 'Authorization']
}));
// 设置速率限制
const rateLimiter = new RateLimiterMemory({
points: 10, // 请求点数
duration: 1, // 每秒
});
app.use(async (req, res, next) => {
try {
await rateLimiter.consume(req.ip);
next();
} catch {
res.status(429).send('请求过多,请稍后再试');
}
});
测试
单元测试
# 安装测试工具
npm install --save-dev mocha chai supertest
# 创建测试文件 (test/users.js)
const request = require('supertest');
const { expect } = require('chai');
const app = require('../app');
describe('User API', () => {
it('should get all users', async () => {
const res = await request(app).get('/api/users');
expect(res.status).to.equal(200);
expect(res.body).to.be.an('array');
});
it('should create a new user', async () => {
const user = {
name: 'Test User',
email: 'test@example.com',
password: 'password123'
};
const res = await request(app)
.post('/api/users')
.send(user);
expect(res.status).to.equal(201);
expect(res.body).to.have.property('_id');
expect(res.body.name).to.equal(user.name);
});
});
运行测试
npx mocha test
示例项目: RESTful API
完整的 Express.js RESTful API 示例:
// app.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
const helmet = require('helmet');
const morgan = require('morgan');
require('dotenv').config();
// 创建 Express 应用
const app = express();
const port = process.env.PORT || 3000;
// 中间件
app.use(helmet());
app.use(cors());
app.use(morgan('dev'));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));
// 连接到数据库
mongoose.connect(process.env.DATABASE_URL)
.then(() => console.log('已连接到 MongoDB'))
.catch(err => console.error('数据库连接错误:', err));
// 路由
const usersRouter = require('./routes/users');
const authRouter = require('./routes/auth');
app.use('/api/users', usersRouter);
app.use('/api/auth', authRouter);
// 基础路由
app.get('/', (req, res) => {
res.send('API 运行中');
});
// 错误处理
app.use((req, res, next) => {
res.status(404).send('未找到资源');
});
app.use((err, req, res, next) => {
console.error(err.stack);
res.status(500).send('服务器错误');
});
// 启动服务器
app.listen(port, () => {
console.log(`服务器运行在 http://localhost:${port}`);
});
module.exports = app;
总结
Express.js 是一个功能强大且灵活的 Web 框架,非常适合构建 RESTful API 和 Web 应用。本文涵盖了 Express 开发中的核心概念和最佳实践,包括路由、中间件、数据库集成、身份验证和部署等方面。
通过掌握这些概念和技术,你可以使用 Express 构建出高性能、安全可靠的 Web 应用和 API 服务。随着应用规模的扩大,合理的项目结构和最佳实践将有助于维护代码质量和开发效率。